<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 5.4.2">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha256-HtsXJanqjKTc8vVQjO4YMhiqFoXkfBsjBWcX91T1jr8=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"xiaoqingming18.github.io","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.15.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>

    <meta name="description" content="记录我的学习、生活和恋爱日常">
<meta property="og:type" content="website">
<meta property="og:title" content="鸣蜩十七">
<meta property="og:url" content="http://xiaoqingming18.github.io/index.html">
<meta property="og:site_name" content="鸣蜩十七">
<meta property="og:description" content="记录我的学习、生活和恋爱日常">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="鸣蜩十七">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://xiaoqingming18.github.io/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>鸣蜩十七</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>

<style>.github-emoji { position: relative; display: inline-block; width: 1.2em; min-height: 1.2em; overflow: hidden; vertical-align: top; color: transparent; }  .github-emoji > span { position: relative; z-index: 10; }  .github-emoji img, .github-emoji .fancybox { margin: 0 !important; padding: 0 !important; border: none !important; outline: none !important; text-decoration: none !important; user-select: none !important; cursor: auto !important; }  .github-emoji img { height: 1.2em !important; width: 1.2em !important; position: absolute !important; left: 50% !important; top: 50% !important; transform: translate(-50%, -50%) !important; user-select: none !important; cursor: auto !important; } .github-emoji-fallback { color: inherit; } .github-emoji-fallback img { opacity: 0 !important; }</style>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">鸣蜩十七</h1>
      <i class="logo-line"></i>
    </a>
      <p class="site-subtitle" itemprop="description">鸣蜩十七是和女朋友谈恋爱的日子</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="fa fa-user fa-fw"></i>关于</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li><li class="menu-item menu-item-schedule"><a href="/schedule/" rel="section"><i class="fa fa-calendar fa-fw"></i>日程表</a></li><li class="menu-item menu-item-sitemap"><a href="/sitemap.xml" rel="section"><i class="fa fa-sitemap fa-fw"></i>站点地图</a></li><li class="menu-item menu-item-commonweal"><a href="/404/" rel="section"><i class="fa fa-heartbeat fa-fw"></i>公益 404</a></li>
  </ul>
</nav>




</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">鸣蜩十七</p>
  <div class="site-description" itemprop="description">记录我的学习、生活和恋爱日常</div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">43</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">8</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">37</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/07/31/Vue%EF%BC%9A%E8%B7%AF%E7%94%B1VueRouter/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/07/31/Vue%EF%BC%9A%E8%B7%AF%E7%94%B1VueRouter/" class="post-title-link" itemprop="url">Vue：路由VueRouter</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-07-31 16:59:56 / 修改时间：19:58:14" itemprop="dateCreated datePublished" datetime="2023-07-31T16:59:56+08:00">2023-07-31</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-前端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="路由的介绍"><a href="#路由的介绍" class="headerlink" title="路由的介绍"></a>路由的介绍</h1><p>各个框架都有自己的路由，Vue中的路由实现就是VueRouter。</p>
<p>路由就是路径与组件的映射关系。</p>
<h1 id="VueRouter的使用"><a href="#VueRouter的使用" class="headerlink" title="VueRouter的使用"></a>VueRouter的使用</h1><h2 id="VueRouter的导入与注册"><a href="#VueRouter的导入与注册" class="headerlink" title="VueRouter的导入与注册"></a>VueRouter的导入与注册</h2><p>首先要下载<code>VueRouter</code>模块到项目中，可以使用以下命令：</p>
<figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm/yarn add vue-router@3.6.5</span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>Vue2中要使用VueRouter3，Vue3中要使用VueRouter4</p>
</blockquote>
<p>随后在<code>main.js</code>中导入路由模块：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">VueRouter</span> <span class="keyword">from</span> <span class="string">'vue-router'</span></span><br></pre></td></tr></tbody></table></figure>
<p>接着将路由模块注册到<code>Vue</code>：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="title class_">Vue</span>.<span class="title function_">use</span>(<span class="title class_">VueRouter</span>)</span><br></pre></td></tr></tbody></table></figure>
<p>之后创建路由对象：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> <span class="title class_">VueRouter</span>()</span><br></pre></td></tr></tbody></table></figure>
<p>最后将路由对象注册到<code>Vue</code>实例中，建立关联：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">Vue</span>({</span><br><span class="line">    <span class="attr">render</span>: <span class="function"><span class="params">h</span> =&gt;</span> <span class="title function_">h</span>(<span class="title class_">App</span>),</span><br><span class="line">    <span class="attr">router</span>: router</span><br><span class="line">}).$amount(<span class="string">'#app'</span>)</span><br></pre></td></tr></tbody></table></figure>
<h2 id="VueRouter的使用-1"><a href="#VueRouter的使用-1" class="headerlink" title="VueRouter的使用"></a>VueRouter的使用</h2><p>完成上面对<code>VueRouter</code>的导入与注册之后便可以使用<code>VueRouter</code>了。</p>
<p>首先，在<code>views</code>目录下创建需要的<strong>页面组件</strong>，并在<code>main.js</code>文件中创建路由对象时传入路由规则配置：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">Find</span> <span class="keyword">from</span> <span class="string">'./views/Find.vue'</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">My</span> <span class="keyword">from</span> <span class="string">'./views/My.vue'</span></span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> <span class="title class_">VueRouter</span>({</span><br><span class="line">    <span class="attr">routes</span>: [</span><br><span class="line">        {<span class="attr">path</span>: <span class="string">'/find'</span>,<span class="attr">component</span>: <span class="title class_">Find</span>},</span><br><span class="line">        {<span class="attr">path</span>: <span class="string">'/my'</span>,<span class="attr">component</span>: <span class="title class_">My</span>}</span><br><span class="line">    ]</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>与页面组件对应的是复用组件，复用组件放在component目录下</p>
</blockquote>
<p>接着，配置路由出口，即配置导航：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#/find"</span>&gt;</span>发现<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">a</span> <span class="attr">href</span>=<span class="string">"#/my"</span>&gt;</span>我的<span class="tag">&lt;/<span class="name">a</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">router-view</span>&gt;</span><span class="tag">&lt;/<span class="name">router-view</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>由于路由组建VueRouter默认会在域名后添加#，所以这里的a标签的href属性路径要以#开头</p>
</blockquote>
<p>当点击不同的链接时，下面的<code>&lt;router-view&gt;</code>标签会替换成对应的组件。</p>
<h1 id="路由模块化"><a href="#路由模块化" class="headerlink" title="路由模块化"></a>路由模块化</h1><p>前面介绍的方法路由的各种配置都堆叠在<code>main.js</code>文件中，十分冗余，下面介绍路由模块化来解决这个问题。</p>
<p>新建<code>router</code>目录，并在该目录下新建<code>index.js</code>文件，在该文件中做以下工作：</p>
<ol>
<li>导入页面组件和<code>Vue</code></li>
<li>导入<code>VueRouter</code>并注册</li>
<li>实例化<code>VueRouter</code>并配置路由规则</li>
<li>暴露router对象</li>
</ol>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="title class_">Vue</span> <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">VueRouter</span> <span class="keyword">from</span> <span class="string">'vue-router'</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Find</span> <span class="keyword">from</span> <span class="string">'../views/Find.vue'</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">My</span> <span class="keyword">from</span> <span class="string">'../views/My.vue'</span></span><br><span class="line"><span class="title class_">Vue</span>.<span class="title function_">use</span>(<span class="title class_">VueRouter</span>)</span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> <span class="title class_">VueRouter</span>({</span><br><span class="line">    <span class="attr">routes</span>: [</span><br><span class="line">        {<span class="attr">path</span>: <span class="string">'/find'</span>,<span class="attr">component</span>: <span class="title class_">Find</span>},</span><br><span class="line">        {<span class="attr">path</span>: <span class="string">'/my'</span>,<span class="attr">component</span>: <span class="title class_">My</span>}</span><br><span class="line">    ]</span><br><span class="line">})</span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> router</span><br></pre></td></tr></tbody></table></figure>
<p>在<code>main.js</code>文件中做以下工作：</p>
<ol>
<li>导入<code>router</code></li>
<li>在实例化<code>Vue</code>对象时传入<code>router</code>配置</li>
</ol>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> router <span class="keyword">from</span> <span class="string">'./router/index'</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Vue</span>({</span><br><span class="line">    <span class="attr">render</span>: <span class="function"><span class="params">h</span> =&gt;</span> <span class="title function_">h</span>(<span class="title class_">App</span>),</span><br><span class="line">    <span class="attr">router</span>: router</span><br><span class="line">}).$amount(<span class="string">'#app'</span>)</span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>路由模块化之后，组件的路径需要跨一级目录，可以使用@符号直接映射到项目的src目录</p>
<p>例如上面的导入组件语句可以换为 import Find from ‘@/views/Find.vue’</p>
</blockquote>
<h1 id="声明式导航（导航链接）"><a href="#声明式导航（导航链接）" class="headerlink" title="声明式导航（导航链接）"></a>声明式导航（导航链接）</h1><h2 id="router-link标签介绍"><a href="#router-link标签介绍" class="headerlink" title="router-link标签介绍"></a>router-link标签介绍</h2><p>声明式导航就是使用<code>&lt;router-link&gt;</code>标签替代<code>&lt;a&gt;</code>标签。</p>
<p>与<code>&lt;a&gt;</code>标签类似，<code>&lt;router-link&gt;</code>可以通过<code>to</code>属性来实现跳转，并且该属性值可以省略路径前缀<code>#</code>：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">router-link</span> <span class="attr">to</span>=<span class="string">"/find"</span>&gt;</span><span class="tag">&lt;/<span class="name">router-link</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<p>此外，<code>&lt;router-link&gt;</code>标签还可以通过自动激活的类名实现高亮。具体来说一下，<code>&lt;router-link&gt;</code>标签渲染后也是一个<code>&lt;a&gt;</code>标签，但是会自动为其添加两个类名：</p>
<ol>
<li><code>router-link-active</code>：模糊匹配，例如<code>to="/my"</code>可以匹配<code>/my</code>、<code>/my/a</code>等。</li>
<li><code>router-link-exact-active</code>：精准匹配，例如<code>to="/my"</code>只可以匹配<code>/my</code>。</li>
</ol>
<p>我们可以通过手动编写上述两个类名对应的样式实现导航选中高亮的效果。</p>
<p>上面说的两个类名太长了，我们可以在<code>router</code>的配置项中为这两个类名指定我们自己想要的类名:</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> <span class="title class_">VueRouter</span>({</span><br><span class="line">    <span class="attr">routes</span>: [···],</span><br><span class="line">    <span class="attr">linkActiveClass</span>: <span class="string">"active"</span>,</span><br><span class="line">    <span class="attr">linkExactActiveClass</span>: <span class="string">"exactActive"</span></span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure>
<h2 id="跳转传参"><a href="#跳转传参" class="headerlink" title="跳转传参"></a>跳转传参</h2><h3 id="1-查询参数传参"><a href="#1-查询参数传参" class="headerlink" title="1.查询参数传参"></a>1.查询参数传参</h3><p>在<code>&lt;router-link&gt;</code>标签中按以下格式传入查询参数：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">router-link</span> <span class="attr">to</span>=<span class="string">"/path?参数1=值1&amp;参数2=值2"</span>&gt;</span><span class="tag">&lt;/<span class="name">router-link</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<p>在对应跳转到的页面组件中用<code>$route.query</code>接收参数：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>{{$route.query.参数1}}<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<h3 id="2-动态路由传参"><a href="#2-动态路由传参" class="headerlink" title="2.动态路由传参"></a>2.动态路由传参</h3><p>首先配置动态路由，即在配置路由规则时设置好需要的参数：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> <span class="title class_">VueRouter</span>({</span><br><span class="line">    <span class="attr">routes</span>: [</span><br><span class="line">        ···</span><br><span class="line">        {<span class="attr">path</span>: <span class="string">'/find/:参数名'</span>,<span class="attr">component</span>: <span class="title class_">Find</span>}</span><br><span class="line">    ]</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure>
<p>然后可以在<code>&lt;router-link&gt;</code>标签中按以下格式传入参数：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">router-link</span> <span class="attr">to</span>=<span class="string">"/path/参数值"</span>&gt;</span><span class="tag">&lt;/<span class="name">router-link</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<p>在对应跳转到的页面组件中用<code>$route.params</code>接收参数：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">h1</span>&gt;</span>{{$route.params.参数1}}<span class="tag">&lt;/<span class="name">h1</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>查询参数传参可以传多个参数，而动态路由传参只可以传一个参数</p>
</blockquote>
<h1 id="编程式导航"><a href="#编程式导航" class="headerlink" title="编程式导航"></a>编程式导航</h1><p>上面的声明式导航适合用于构建底部<code>tabBar</code>导航的页面，但更多的情况是要点击按钮后跳转到指定的组件，这就需要使用编程式导航，下面详细讲解。</p>
<h2 id="编程式导航的两种语法"><a href="#编程式导航的两种语法" class="headerlink" title="编程式导航的两种语法"></a>编程式导航的两种语法</h2><h3 id="1-path路径跳转"><a href="#1-path路径跳转" class="headerlink" title="1.path路径跳转"></a>1.path路径跳转</h3><p>通过<code>$router.push()</code>方法跳转到指定路由页面组件：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"$router.push('路由路径path')"</span>&gt;</span><span class="tag">&lt;/<span class="name">button</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>如果是在vue的methods配置项自定义的方法中跳转，不要忘记需要用this.$route.push()方法，因为在模板内的this是默认指向vue实例的，所以可以省略</p>
</blockquote>
<h3 id="2-命名路由跳转"><a href="#2-命名路由跳转" class="headerlink" title="2. 命名路由跳转"></a>2. 命名路由跳转</h3><p>先在实例化<code>VueRouter</code>时的<code>routes</code>配置项中为每一个路由规则命名：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">{<span class="attr">name</span>: <span class="string">'路由名'</span>,<span class="attr">path</span>: <span class="string">'路由路径'</span>,<span class="attr">component</span>: 页面组件}</span><br></pre></td></tr></tbody></table></figure>
<p>然后通过<code>$router.push()</code>方法跳转到指定路由页面组件，和第一种方法的形式不同：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">$router</span>.<span class="title function_">push</span>({</span><br><span class="line">    <span class="attr">name</span>: <span class="string">'路由名'</span></span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure>
<h2 id="编程式导航的两种传参方式："><a href="#编程式导航的两种传参方式：" class="headerlink" title="编程式导航的两种传参方式："></a>编程式导航的两种传参方式：</h2><p>编程式导航有两种传参方式：</p>
<ol>
<li>查询参数传参</li>
<li>动态路由传参</li>
</ol>
<p>下面针对编程式导航的两种语法分别演示一下这两种传参方式如何使用。</p>
<h3 id="1-path路径跳转方式对应的两种传参方法"><a href="#1-path路径跳转方式对应的两种传参方法" class="headerlink" title="1.path路径跳转方式对应的两种传参方法"></a>1.path路径跳转方式对应的两种传参方法</h3><p>path路径跳转的查询参数传参如下所示：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">$router</span>.<span class="title function_">push</span>(<span class="string">'/路由路径?参数1=值1&amp;参数2=值2'</span>)</span><br></pre></td></tr></tbody></table></figure>
<p>也可以用另一种方式：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">$router</span>.<span class="title function_">push</span>({</span><br><span class="line">    <span class="attr">path</span>: <span class="string">'路由路径'</span>,</span><br><span class="line">    <span class="attr">query</span>: {</span><br><span class="line">        参数<span class="number">1</span>:值<span class="number">1</span>,</span><br><span class="line">        参数<span class="number">2</span>:值<span class="number">2</span></span><br><span class="line">    }</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure>
<p>在对应的页面组件通过<code>$route.query.参数名</code>获取参数值。</p>
<p>path路径跳转对应的动态路由传参如下所示：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">$router</span>.<span class="title function_">push</span>(<span class="string">'路由路径/参数值'</span>)</span><br></pre></td></tr></tbody></table></figure>
<p>或：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">$router</span>.<span class="title function_">push</span>({</span><br><span class="line">    <span class="attr">path</span>: <span class="string">'路由路径/参数值'</span></span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure>
<p>在对应页面组件通过<code>$route.params.参数名</code>获取参数值。</p>
<h3 id="2-命名路由跳转方式对应的两种传参方法"><a href="#2-命名路由跳转方式对应的两种传参方法" class="headerlink" title="2. 命名路由跳转方式对应的两种传参方法"></a>2. 命名路由跳转方式对应的两种传参方法</h3><p>首先是查询参数传参：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">$router</span>.<span class="title function_">push</span>({</span><br><span class="line">    <span class="attr">name</span>: <span class="string">'路由名'</span>,</span><br><span class="line">    <span class="attr">query</span>: {</span><br><span class="line">        参数名<span class="number">1</span>: 值<span class="number">1</span>,</span><br><span class="line">        参数名<span class="number">2</span>: 值<span class="number">2</span></span><br><span class="line">    }</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure>
<p>在对应页面组件通过<code>$route.query.参数名</code>获取对应参数。</p>
<p>然后是动态路由传参：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">this</span>.<span class="property">$router</span>.<span class="title function_">push</span>({</span><br><span class="line">    <span class="attr">name</span>: <span class="string">'路由名'</span>,</span><br><span class="line">    <span class="attr">params</span>: {</span><br><span class="line">        参数名: 参数值</span><br><span class="line">    }</span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure>
<p>在对应页面组件通过<code>$route.params.参数名</code>获取参数值。</p>
<h1 id="路由模式设置"><a href="#路由模式设置" class="headerlink" title="路由模式设置"></a>路由模式设置</h1><p>路由路径中的<code>#</code>看起来使<code>URL</code>很不自然，可以通过更改路由模式去掉。</p>
<p>共有两种路由模式：</p>
<ol>
<li><code>hash</code>路由：默认的路由模式，<code>URL</code>中有<code>#</code>。</li>
<li><code>history</code>路由：常用这种模式，<code>URL</code>中无<code>#</code>，但上线后需要服务端支持。</li>
</ol>
<p>可以在实例化<code>VueRouter</code>时传入<code>mode</code>配置项来更换路由模式：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> <span class="title class_">VueRouter</span>({</span><br><span class="line">    <span class="attr">mode</span>: <span class="string">'history'</span></span><br><span class="line">})</span><br></pre></td></tr></tbody></table></figure>
<h1 id="路由的一些应用"><a href="#路由的一些应用" class="headerlink" title="路由的一些应用"></a>路由的一些应用</h1><h2 id="重定向"><a href="#重定向" class="headerlink" title="重定向"></a>重定向</h2><p>在实例化<code>VueRouter</code>的配置项<code>routes</code>中添加<code>redirect</code>重定向路由规则：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">{<span class="attr">path</span>: 路径, <span class="attr">redirect</span>: 重定向到的路径}</span><br></pre></td></tr></tbody></table></figure>
<p>之后访问前面的路径会重定向到后面的路径。</p>
<h2 id="404页面"><a href="#404页面" class="headerlink" title="404页面"></a>404页面</h2><p>在实例化<code>VueRouter</code>的配置项<code>routes</code>中最后一个位置添加一条路由规则，其中<code>path</code>为通配符<code>*</code>，就可以实现404页面，当其上面所有的路由规则都不匹配时，则会跳转到指定的404组件页面：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">{<span class="attr">path</span>: <span class="string">'*'</span>,<span class="attr">component</span>: <span class="number">404</span>页面组件}</span><br></pre></td></tr></tbody></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/07/20/Vue%E5%85%A5%E9%97%A8%E7%AC%94%E8%AE%B0/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/07/20/Vue%E5%85%A5%E9%97%A8%E7%AC%94%E8%AE%B0/" class="post-title-link" itemprop="url">Vue入门笔记</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-07-20 14:14:14 / 修改时间：15:47:19" itemprop="dateCreated datePublished" datetime="2023-07-20T14:14:14+08:00">2023-07-20</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="组件"><a href="#组件" class="headerlink" title="组件"></a>组件</h1><h2 id="区分组件化和模块化"><a href="#区分组件化和模块化" class="headerlink" title="区分组件化和模块化"></a>区分组件化和模块化</h2><p>模块化：当应用中的js都以模块来编写的，那这个应用就是一个模块化的应用。</p>
<p>组件化：当应用中的功能都以组件的方式来编写，那这个应用就是一个组件化的应用。</p>
<h2 id="什么是组件"><a href="#什么是组件" class="headerlink" title="什么是组件"></a>什么是组件</h2><p>组件允许我们将 UI 划分为独立的、可重用的部分，并且可以对每个部分进行单独开发。在实际应用中，组件常常被组织成层层嵌套的树状结构：</p>
<p><img src="/2023/07/20/Vue%E5%85%A5%E9%97%A8%E7%AC%94%E8%AE%B0/组件嵌套.png" alt="组件嵌套结构"></p>
<p>总结一下，组件可以：</p>
<ol>
<li>用来实现局部特定功能效果的代码集合(html/css/js/静态资源)。</li>
<li>将复杂的界面简单化。</li>
<li>复用编码，简化项目编码，提高运行效率</li>
</ol>
<h2 id="非单文件组件"><a href="#非单文件组件" class="headerlink" title="非单文件组件"></a>非单文件组件</h2><h3 id="非单文件组件的定义"><a href="#非单文件组件的定义" class="headerlink" title="非单文件组件的定义"></a>非单文件组件的定义</h3><p>首先，使用<code>Vue.extend({options})</code>方法<strong>定义组件</strong>。其中，传入的<code>options</code>配置对象与创建<code>Vue</code>实例传入的配置对象很相似，唯一的区别就是组件的配置对象没有<code>el</code>属性，并且组件配置对象的<code>data</code>属性必须采用函数式写法，原因如下：</p>
<ol>
<li>为什么没有<code>el</code>属性：最终所有的组件都要经过一个<code>Vue</code>实例的管理，由<code>Vue</code>实例中的<code>el</code>属性决定服务哪个容器。</li>
<li>为什么<code>data</code>要用函数式写法：避免组件被复用时，数据存在引用关系。</li>
</ol>
<h3 id="非单文件组件的注册"><a href="#非单文件组件的注册" class="headerlink" title="非单文件组件的注册"></a>非单文件组件的注册</h3><p>非单文件组件的注册分为两种，局部注册和全局注册，我们先来看看局部注册。</p>
<p>局部注册就是在创建<code>Vue</code>实例时在配置对象中添加一个<code>components</code>属性，并在其中添加创建好的组件。局部注册的组件只能在当前注册的范围内使用</p>
<p>全局注册就是用<code>Vue.component(‘组件名’，组件)</code>方法来注册。全局注册的组件可以在全局任何一个组件内使用</p>
<h3 id="非单文件组件的使用"><a href="#非单文件组件的使用" class="headerlink" title="非单文件组件的使用"></a>非单文件组件的使用</h3><p>经过组件的定义和注册之后，就可以通过直接写组件标签来使用组件了，下面我们通过一个例子完整展示非单文件组件的使用：</p>
<figure class="highlight html"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">body</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">div</span> <span class="attr">id</span>=<span class="string">"root"</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!--第三步: 使用组件--&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">school</span>&gt;</span><span class="tag">&lt;/<span class="name">school</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">script</span>&gt;</span><span class="language-handlebars"><span class="language-xml"></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        // 第一步:定义组件</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            // student组件全局注册，让他可以在school组件中使用</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        const student = Vue.extend({</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            template: `</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                <span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                    <span class="tag">&lt;<span class="name">h2</span>&gt;</span>学生名称:</span><span class="template-variable">{{<span class="name">name</span>}}</span><span class="language-xml"><span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                    <span class="tag">&lt;<span class="name">h2</span>&gt;</span>所属专业:</span><span class="template-variable">{{<span class="name">speciality</span>}}</span><span class="language-xml"><span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            `,</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            data() {</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                return {</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                    name: '鸣蜩十七',</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                    speciality: '计算机科学与技术'</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                }</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            }</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        });</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        // 第二步:注册组件（全局注册）</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        Vue.component('student',student);</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            // school组件后面在Vue实例中局部注册，这样只能在root标签下使用</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        const school = Vue.extend({</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            template: `</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                <span class="tag">&lt;<span class="name">div</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                    <span class="tag">&lt;<span class="name">h2</span>&gt;</span>学校名称:</span><span class="template-variable">{{<span class="name">name</span>}}</span><span class="language-xml"><span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                    <span class="tag">&lt;<span class="name">h2</span>&gt;</span>学校地址:</span><span class="template-variable">{{<span class="name">address</span>}}</span><span class="language-xml"><span class="tag">&lt;/<span class="name">h2</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                    <span class="tag">&lt;<span class="name">student</span>&gt;</span><span class="tag">&lt;/<span class="name">student</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                <span class="tag">&lt;/<span class="name">div</span>&gt;</span></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            `,</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            data() {</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                return {</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                    name: '巢湖学院',</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                    address: '巢湖'</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">                }</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            }</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        });</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars"></span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        new Vue({</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            el: '#root',</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            // 第二步: 注册组件（局部注册）</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">            components: {school}</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">        });</span></span></span><br><span class="line"><span class="language-xml"><span class="language-handlebars">    </span></span><span class="tag">&lt;/<span class="name">script</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">body</span>&gt;</span></span><br></pre></td></tr></tbody></table></figure>
<h3 id="全局注册与局部注册详解"><a href="#全局注册与局部注册详解" class="headerlink" title="全局注册与局部注册详解"></a>全局注册与局部注册详解</h3><p>上面已经说过了被全局注册的组件，可以在全局任何一个组件内使用，被局部注册的组件，只能在当前注册的范围内使用。下面我们基于上面的例子详细讲解一下全局注册与局部注册。</p>
<p>首先我们要知道，上面例子中<code>student</code>是全局注册的，<code>school</code>是局部注册的，所以可以在<code>school</code>组件的模板<code>template</code>中直接使用<code>&lt;student&gt;</code>标签。</p>
<p>进一步，由于<code>school</code>组件是在<code>Vue</code>实例中局部注册的，所以我们只能在<code>Vue</code>实例绑定的<code>#root</code>标签中使用<code>&lt;school&gt;</code>标签，如果在<code>student</code>的组件模板中使用<code>&lt;school&gt;</code>标签就会报错。</p>
<h3 id="注意项"><a href="#注意项" class="headerlink" title="注意项"></a>注意项</h3><ol>
<li>组件名由一个单词组成时有两种写法：<ul>
<li>第一种写法是首字母小写：school</li>
<li>第二种写法是首字母大写：School</li>
</ul>
</li>
<li>组件名由两个单词组成时也有两种写法：<ul>
<li>第一种写法是kebab-case命名：my-school</li>
<li>第二种写法是CamelCase命名：MySchool (<strong>需要Vue脚手架支持</strong>)</li>
</ul>
</li>
<li>组件名尽可能回避HTML中已有的元素名称</li>
<li><strong>可以在组件配置对象中使用<code>name</code>配置项指定组件在开发者工具中呈现的名字</strong></li>
<li><strong>在脚手架环境中</strong>，组件标签可以简写为<code>&lt;组件名/&gt;</code></li>
</ol>
<h2 id="VueComponents"><a href="#VueComponents" class="headerlink" title="VueComponents"></a>VueComponents</h2><p>组件本质是一个由<code>Vue.extend()</code>方法返回的一个<code>VueComponent</code>构造函数，然后注册组件时调用的<code>Vue.component()</code>方法会调用刚才返回的构造函数实例化一个组件对象。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/07/16/JavaScript%EF%BC%9A%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E4%B8%8E%E5%8E%9F%E5%9E%8B%E3%80%81%E5%8E%9F%E5%9E%8B%E9%93%BE/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/07/16/JavaScript%EF%BC%9A%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E4%B8%8E%E5%8E%9F%E5%9E%8B%E3%80%81%E5%8E%9F%E5%9E%8B%E9%93%BE/" class="post-title-link" itemprop="url">JavaScript：构造函数与原型、原型链</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-07-16 13:50:27 / 修改时间：17:04:21" itemprop="dateCreated datePublished" datetime="2023-07-16T13:50:27+08:00">2023-07-16</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-前端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="构造函数"><a href="#构造函数" class="headerlink" title="构造函数"></a>构造函数</h1><h2 id="构造函数介绍"><a href="#构造函数介绍" class="headerlink" title="构造函数介绍"></a>构造函数介绍</h2><p>在JavaScript中，构造函数与普通函数是一样的，为了区分普通函数与构造函数，人为规定构造函数首字母要大写，并且<strong>构造函数不需要显式地<code>return</code>实例</strong>。下面是一个使用构造函数创建实例对象的例子：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name,age,job</span>) {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">job</span> = job;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="title function_">sayName</span>() = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line">        <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">name</span>);</span><br><span class="line">    };</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> aMan = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"小清明十八"</span>,<span class="number">20</span>,<span class="string">"前端炮灰"</span>);</span><br><span class="line">aMan.<span class="title function_">sayName</span>(); <span class="comment">// 小清明十八</span></span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>构造函数不是只能用函数声明的形式，也可以将构造函数赋值给变量。</p>
<p>实例化时，构造函数后面的括号可加可不加。</p>
</blockquote>
<p>当时用<code>New</code>操作符创建实例对象时，调用构造函数会执行如下操作：</p>
<ol>
<li>在内存中创建一个对象。</li>
<li>这个新对象内部的<code>[[Prototype]]</code>（即<code>__proto__属性</code>）特性被赋值为构造函数的<code>prototype</code>属性。说人话就是新对象的<code>__proto__</code>属性指向原型对象。</li>
<li>构造函数内部的<code>this</code>被赋值为这个新对象，即<code>this</code>指向新对象。</li>
<li>执行构造函数内部的代码，即给新对象添加属性和方法。</li>
<li>隐式返回刚刚创建的新对象。</li>
</ol>
<blockquote>
<p>在构造函数中定义的属性和方法最终会添加到实例对象上！</p>
</blockquote>
<p>通过构造函数创建的每个实例对象都有一个<code>constructor</code>属性，指向构造函数。所以可以使用<code>instancof</code>操作符确定对象类型，比如在上面的例子中：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(aMan <span class="title function_">instanceof</span>(<span class="title class_">Person</span>)); <span class="comment">// true</span></span><br></pre></td></tr></tbody></table></figure>
<h2 id="构造函数存在的问题"><a href="#构造函数存在的问题" class="headerlink" title="构造函数存在的问题"></a>构造函数存在的问题</h2><p>构造函数存在的主要问题是，<strong>其定义的方法会在每个实例上都创建一遍，浪费内存</strong>。因此，同一个构造函数创建的不同实例对象的同名函数并不相等：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> person1 = <span class="keyword">new</span> <span class="title class_">Person</span>(···);</span><br><span class="line"><span class="keyword">let</span> person2 = <span class="keyword">new</span> <span class="title class_">Person</span>(···);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person1.<span class="property">sayName</span> == person2.<span class="property">sayName</span>); <span class="comment">// false</span></span><br></pre></td></tr></tbody></table></figure>
<p>这个问题可以通过下面要介绍的原型模式来解决。</p>
<h1 id="原型模式"><a href="#原型模式" class="headerlink" title="原型模式"></a>原型模式</h1><h2 id="原型对象"><a href="#原型对象" class="headerlink" title="原型对象"></a>原型对象</h2><p>无论何时，只要创建一个函数，就会按照特定的规则自动为这个函数创建一个<code>prototype</code>属性，这个属性是一个对象，称为<strong>原型对象</strong>，这个对象的作用是实现对象属性和方法的共享，这很好的解决了构造函数带来的内存浪费问题。因为<strong>原型对象包含由特定引用类型的实例共享的属性和方法</strong>。</p>
<p>我们可以将实例对象共有的属性和方法添加到构造函数对应的原型对象上面。例如前面的<code>Person</code>例子，我们可以将<code>sayName</code>方法添加到构造函数的原型对象上面实现共享：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name,age,job</span>) {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">job</span> = job;</span><br><span class="line">}</span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="title function_">sayName</span>() = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">name</span>);</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure>
<blockquote>
<p>注意，这里是使用点语法将方法添加到<code>prototype</code>原型对象上面，而不是直接给<code>prototype</code>赋值一个对象，不能这么做的原因在后面会细说。</p>
</blockquote>
<h2 id="constructor属性"><a href="#constructor属性" class="headerlink" title="constructor属性"></a><code>constructor</code>属性</h2><p>默认情况下，所有原型对象自动获得一个名为<code>constructor</code>的属性，这个属性<strong>指回与之关联的构造函数</strong>。上面的例子中，<code>Person.prototype.constructor</code>指回构造函数<code>Person</code>。</p>
<h2 id="Prototype-特性与-proto-属性"><a href="#Prototype-特性与-proto-属性" class="headerlink" title="[[Prototype]]特性与__proto__属性"></a><code>[[Prototype]]</code>特性与<code>__proto__</code>属性</h2><p>任意一个由构造函数实例化的实例对象都有一个<code>__proto__</code>属性，这个属性指向实例对象的构造函数的原型对象。例如前面的例子，<code>person1.__proto__</code>指向<code>Person.prototype</code>。</p>
<p>那么<code>[[Prototype]]</code>特性是什么呢？其实它也是实例对象所有的，它是一个指针。每次调用构造函数创建一个实例时，这个实例的内部<code>[[Prototype]]</code>指针就会被赋值为构造函数的原型对象。但我们并不能直接访问这个特性（指针），所以便有了<code>__proto__</code>属性供我们访问实例对象对应的构造函数的原型对象。</p>
<blockquote>
<p><strong>任意一个构造函数实例化的对象</strong>，<strong>都有一个<strong>proto</strong>属性（<strong>proto</strong>并非标准属性，ECMA-262第5版将该属性或指针称为[[Prototype]]，可通过Object.getPrototypeOf()标准方法访问该属性）</strong>，指向构造函数的原型对象。—-<a target="_blank" rel="noopener" href="http://home.cnblogs.com/u/sinstone/">sinのstone</a></p>
</blockquote>
<h2 id="彻底搞懂prototype、constructor和-proto-之间的关系"><a href="#彻底搞懂prototype、constructor和-proto-之间的关系" class="headerlink" title="彻底搞懂prototype、constructor和__proto__之间的关系"></a>彻底搞懂<code>prototype</code>、<code>constructor</code>和<code>__proto__</code>之间的关系</h2><p>先来看一段代码：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name,age,job</span>) {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">age</span> = age;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">job</span> = job;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="title function_">sayName</span>() = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">name</span>);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> <span class="title class_">Junli</span> = <span class="keyword">new</span> <span class="title class_">Person</span>(<span class="string">"君丽"</span>,<span class="number">18</span>,<span class="string">"小阿飞女盆友"</span>)</span><br></pre></td></tr></tbody></table></figure>
<p>基于上面的代码，用一张图来展示<code>prototype</code>、<code>constructor</code>和<code>__proto__</code>之间的关系：</p>
<p><img src="/2023/07/16/JavaScript%EF%BC%9A%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E4%B8%8E%E5%8E%9F%E5%9E%8B%E3%80%81%E5%8E%9F%E5%9E%8B%E9%93%BE/三者关系.png" alt="三者关系"></p>
<h1 id="原型链"><a href="#原型链" class="headerlink" title="原型链"></a>原型链</h1><p>JavaScript查找对象某个属性遵循以下步骤：</p>
<ol>
<li>现在实例对象本身的属性中查找是否有该属性，若有则直接返回该属性值。</li>
<li>若实例对象本身没有该属性，就到该实例对象对应的原型对象中查找，若有则返回该属性值。</li>
<li>若实例的原型对象也没有该属性，则搜索原型对象的原型。</li>
<li>重复第三步知道搜索到<code>Object</code>为止。</li>
</ol>
<p>上面的步骤就是原型链的基本思想</p>
<h1 id="ES5继承"><a href="#ES5继承" class="headerlink" title="ES5继承"></a>ES5继承</h1><h2 id="原型继承"><a href="#原型继承" class="headerlink" title="原型继承"></a>原型继承</h2><p>ES5继承主要是通过构造函数+原型对象实现的，核心原理就是将子类的原型对象赋值为一个父类实例对象，然后再将子类原型对象的<code>constructor</code>属性赋值为子类的构造函数。即<code>Son.prototype = new Father()</code>和<code>Son.prototype.constructor = Son</code>。这样一来，子类便可以使用父类的属性和方法。</p>
<p>假设有一个现在有一个系统，用户和管理员都可以登录系统，但管理员可以删除一个用户，继承关系实现代码如下：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">User</span>(<span class="params">username,password</span>) {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">username</span> = username;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">password</span> = password;</span><br><span class="line">}</span><br><span class="line"><span class="title class_">User</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">login</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line">     <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"登录"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Admin</span>(<span class="params"></span>) {}</span><br><span class="line"><span class="comment">// 实现继承</span></span><br><span class="line"><span class="title class_">Admin</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line"><span class="title class_">Admin</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">constructor</span> = <span class="title class_">Admin</span>;</span><br><span class="line"><span class="title class_">Admin</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">deletePerson</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"删除用户"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> admin = <span class="keyword">new</span> <span class="title class_">Admin</span>(<span class="string">"admin"</span>, <span class="string">"password"</span>);</span><br><span class="line">admin.<span class="title function_">login</span>();</span><br><span class="line">admin.<span class="title function_">deletePerson</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(admin);</span><br></pre></td></tr></tbody></table></figure>
<p>运行结果如下所示：</p>
<p><img src="/2023/07/16/JavaScript%EF%BC%9A%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E4%B8%8E%E5%8E%9F%E5%9E%8B%E3%80%81%E5%8E%9F%E5%9E%8B%E9%93%BE/继承运行结果1.png" alt="继承运行结果1"></p>
<p>可以发现，已经实现了子类调用父类的方法，即<code>Admin</code>实例调用了<code>User</code>的登录方法。但还有个问题，<code>admin</code>和<code>password</code>两个参数并没能传递给父类<code>User</code>，所以导致<code>Admin</code>实例对象的<code>username</code>和<code>password</code>属性为<code>undefined</code>。</p>
<p>为了解决给父类传参问题我们重新定义利用原型继承的步骤：</p>
<ol>
<li>创建父类构造函数，定义父类的属性和方法。</li>
<li>创建子类构造函数，并在构造函数中调用父类构造函数，使用<code>父类.call()</code>绑定当前子类的this到父类构造函数上，确保子类可以继承父类的属性。</li>
<li>创建父类的实例，并将该实例赋值给子类构造函数的prototype，并将子类原型对象的<code>constructor</code>属性赋值为子类的构造函数。</li>
<li>在子类构造函数的原型中添加子类特有的属性和方法。</li>
</ol>
<blockquote>
<p>额外总结一下，一般将属性放在构造函数内，方法放在原型对象中。</p>
</blockquote>
<p>基于上面的步骤我们重写上面的例子：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">User</span>(<span class="params">username,password</span>) {</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">username</span> = username;</span><br><span class="line">    <span class="variable language_">this</span>.<span class="property">password</span> = password;</span><br><span class="line">}</span><br><span class="line"><span class="title class_">User</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">login</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line">     <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"登录"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Admin</span>(<span class="params">username,password</span>) {</span><br><span class="line">    <span class="title class_">User</span>.<span class="title function_">call</span>(username,password); <span class="comment">// 只改动了这一处</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 实现继承</span></span><br><span class="line"><span class="title class_">Admin</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">User</span>();</span><br><span class="line"><span class="title class_">Admin</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">constructor</span> = <span class="title class_">Admin</span>;</span><br><span class="line"><span class="title class_">Admin</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">deletePerson</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"删除用户"</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> admin = <span class="keyword">new</span> <span class="title class_">Admin</span>(<span class="string">"admin"</span>, <span class="string">"password"</span>);</span><br><span class="line">admin.<span class="title function_">login</span>();</span><br><span class="line">admin.<span class="title function_">deletePerson</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(admin);</span><br></pre></td></tr></tbody></table></figure>
<p>运行结果如下：</p>
<p><img src="/2023/07/16/JavaScript%EF%BC%9A%E6%9E%84%E9%80%A0%E5%87%BD%E6%95%B0%E4%B8%8E%E5%8E%9F%E5%9E%8B%E3%80%81%E5%8E%9F%E5%9E%8B%E9%93%BE/继承运行结果2.png" alt="继承运行结果2"></p>
<h2 id="原型继承的主要问题"><a href="#原型继承的主要问题" class="headerlink" title="原型继承的主要问题"></a>原型继承的主要问题</h2><p>上面所说的原型继承有一个致命问题，<strong>继承过来的实例属性，如果是引用类型，会被多个子类的实例共享</strong>，这意味着所有的子类实例对于该属性的修改都会影响到其他子类实例。这是因为引用类型的属性存储在堆内存中，并且多个实例共用同一个引用地址。</p>
<p>用一个例子来看这个问题：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Person</span>(<span class="params">name</span>) {</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">name</span> = name;</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">hobbies</span> = [<span class="string">'reading'</span>, <span class="string">'swimming'</span>];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title class_">Person</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">sayHello</span> = <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Hello, my name is '</span> + <span class="variable language_">this</span>.<span class="property">name</span>);</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">Student</span>(<span class="params">name, grade</span>) {</span><br><span class="line">  <span class="title class_">Person</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>,name);</span><br><span class="line">  <span class="variable language_">this</span>.<span class="property">grade</span> = grade;</span><br><span class="line">}</span><br><span class="line"><span class="title class_">Student</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line"><span class="title class_">Student</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">constructor</span> = <span class="title class_">Student</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> student1 = <span class="keyword">new</span> <span class="title class_">Student</span>(<span class="string">'Alice'</span>, <span class="number">5</span>);</span><br><span class="line"><span class="keyword">var</span> student2 = <span class="keyword">new</span> <span class="title class_">Student</span>(<span class="string">'Bob'</span>, <span class="number">6</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改student1的hobbies</span></span><br><span class="line">student1.<span class="property">hobbies</span>.<span class="title function_">push</span>(<span class="string">'playing basketball'</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(student1.<span class="property">hobbies</span>);  <span class="comment">// ['reading', 'swimming', 'playing basketball']</span></span><br><span class="line"><span class="comment">// 影响了student2的hobbies</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(student2.<span class="property">hobbies</span>);  <span class="comment">// ['reading', 'swimming', 'playing basketball']</span></span><br></pre></td></tr></tbody></table></figure>
<p>上面例子中修改<code>student1</code>的<code>hobbies</code>属性影响到了<code>student2</code>的<code>hobbies</code>属性。</p>
<p>为了解决上面的问题，可以使用其他的解决方式或继承方式，比如<strong>盗用构造函数</strong>、<strong>原型继承</strong>、<strong>组合继承</strong>、<strong>寄生组合继承</strong>等，这些方式避免了引用类型属性被共享的问题。这些方式以后再补充。</p>
<blockquote>
<p>本文引用下列文章：</p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/jieyucx/article/details/131445345"> javascript原型、原型链、继承详解_js 原型链继承</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/07/14/JavaScript%EF%BC%9A%E5%87%BD%E6%95%B0/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/07/14/JavaScript%EF%BC%9A%E5%87%BD%E6%95%B0/" class="post-title-link" itemprop="url">JavaScript：函数</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-07-14 13:44:34 / 修改时间：14:52:22" itemprop="dateCreated datePublished" datetime="2023-07-14T13:44:34+08:00">2023-07-14</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-前端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="JavaScript函数定义"><a href="#JavaScript函数定义" class="headerlink" title="JavaScript函数定义"></a>JavaScript函数定义</h1><p>JavaScript的函数实际上是对象。每个函数都是<code>Function</code>类型的实例，有自己的属性和方法，函数名就是指向函数对象的指针。</p>
<p>JavaScript的函数有几种定义方式，最常用的便是函数声明：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">sum</span>(<span class="params">num1,num2</span>) {</span><br><span class="line">    <span class="keyword">return</span> num1 + num2;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>另一种定义函数的方式是函数表达式：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> sum = <span class="keyword">function</span>(<span class="params">num1,num2</span>) {</span><br><span class="line">    <span class="keyword">return</span> num1 + num2;</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure>
<p>用函数表达式定义函数相当于将用<code>function</code>定义的函数实例的引用赋给<code>sum</code>对象变量。</p>
<h1 id="箭头函数"><a href="#箭头函数" class="headerlink" title="箭头函数"></a>箭头函数</h1><p>任何使用函数的地方也可以用箭头函数代替，箭头函数的形式如下：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="title function_">sum</span> = (<span class="params">a,b</span>) =&gt; {</span><br><span class="line">    <span class="keyword">return</span> a + b;</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure>
<p>如果只有一个参数，可以省略括号：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="title function_">double</span> = x =&gt; {<span class="keyword">return</span> x ** <span class="number">2</span>;};</span><br></pre></td></tr></tbody></table></figure>
<p>没有参数时不可以省略括号：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> <span class="title function_">getRandom</span> = (<span class="params"></span>) =&gt; {<span class="keyword">return</span> <span class="title class_">Math</span>.<span class="title function_">random</span>();};</span><br></pre></td></tr></tbody></table></figure>
<p>箭头函数虽然语法简洁，但也有很多场合不适用。箭头函数不能使用<code>arguments</code>、<code>super</code>和<code>new.target</code>，也不能用作构造函数。此外，箭头函数也没有<code>prototype</code>属性。</p>
<h1 id="函数名与name属性"><a href="#函数名与name属性" class="headerlink" title="函数名与name属性"></a>函数名与<code>name</code>属性</h1><h2 id="函数名"><a href="#函数名" class="headerlink" title="函数名"></a>函数名</h2><p>函数名就是指向函数的指针，所以一个函数可以有多个函数名，就像一个对象可以被多个对象变量引用一样：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">sum</span>(<span class="params">num1,num2</span>) {</span><br><span class="line">    <span class="keyword">return</span> num1 + num2;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">sum</span>(<span class="number">10</span>,<span class="number">10</span>)); <span class="comment">// 20</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> anotherSum = sum;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">anotherSum</span>(<span class="number">10</span>,<span class="number">10</span>)); <span class="comment">// 20</span></span><br><span class="line"></span><br><span class="line">sum = <span class="literal">null</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">anotherSum</span>(<span class="number">10</span>,<span class="number">10</span>)); <span class="comment">// 20</span></span><br></pre></td></tr></tbody></table></figure>
<h2 id="name属性"><a href="#name属性" class="headerlink" title="name属性"></a><code>name</code>属性</h2><p>所有的函数都有一个<code>name</code>属性，该属性保存的是该函数名称的字符串。</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {}</span><br><span class="line">foo.<span class="property">name</span> <span class="comment">// "foo"</span></span><br></pre></td></tr></tbody></table></figure>
<p>这个属性早就被浏览器广泛支持，但是直到 ES6，才将其写入了标准。但是ES6对这个属性的一些行为做出了一些修改。分以下四种情况：</p>
<p><strong>函数被声明为一个命名函数</strong>：这种情况ES5和ES6语法一致，<code>name</code>属性返回该函数的函数名：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {}</span><br><span class="line"><span class="keyword">typeof</span> foo <span class="comment">// “function”</span></span><br><span class="line">foo.<span class="property">name</span> <span class="comment">// “foo”</span></span><br></pre></td></tr></tbody></table></figure>
<p><strong>创建一个匿名函数，并赋值给一个变量</strong>：在ES5中，该函数的<code>name</code>属性为“”，而 ES6 的<code>name</code>属性会返回实际的函数名：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> f = <span class="keyword">function</span> (<span class="params"></span>) {};</span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line">f.<span class="property">name</span> <span class="comment">// “”</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line">f.<span class="property">name</span> <span class="comment">// “f”</span></span><br></pre></td></tr></tbody></table></figure>
<p><strong>创建一个匿名函数，将其赋值给一个<code>window</code>的属性</strong>：在ES5中，该函数的<code>name</code>属性返回函数名（window下的赋值属性名），而 ES6 的<code>name</code>属性会返回“”：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">window</span>.<span class="property">f</span> = <span class="keyword">function</span> (<span class="params"></span>) { };</span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line">f.<span class="property">name</span> <span class="comment">// “f”</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line">f.<span class="property">name</span> <span class="comment">// “”</span></span><br></pre></td></tr></tbody></table></figure>
<p><strong>创建一个命名函数，将其赋值给一个变量或<code>window</code>下的属性</strong>：这种情况ES5和ES6语法一致，<code>name</code>均返回函数的字面量名称，与将函数进行赋值的变量或属性名称无关：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> f = <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) {};</span><br><span class="line"><span class="variable language_">window</span>.<span class="property">f</span> = <span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params"></span>) { };</span><br><span class="line"><span class="comment">// ES5</span></span><br><span class="line">f.<span class="property">name</span> <span class="comment">// “foo”</span></span><br><span class="line"><span class="comment">// ES6</span></span><br><span class="line">f.<span class="property">name</span> <span class="comment">// “foo”</span></span><br></pre></td></tr></tbody></table></figure>
<h1 id="理解JavaScript函数的参数"><a href="#理解JavaScript函数的参数" class="headerlink" title="理解JavaScript函数的参数"></a>理解JavaScript函数的参数</h1><p>与其他语言不同，JavaScript即不关心参数的个数也不关心参数的数据类型。因为JavaScript函数的参数在内部表现为一个数组。在使用<code>function</code>关键字定义函数（非箭头函数）时，可以在函数内部访问<code>arguments</code>对象，从中取得传进来的每个参数值。</p>
<p>看下面的例子：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">fucntion <span class="title function_">sayHi</span>(<span class="params">name,message</span>) {</span><br><span class="line">    <span class="variable language_">console</span>,<span class="title function_">log</span>(<span class="string">"Hello "</span> + name + <span class="string">","</span> + message);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>上面的函数可以写成下面这样：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">sayHi</span>(<span class="params"></span>) {</span><br><span class="line">    <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"Hello "</span> + name + <span class="string">","</span> + <span class="variable language_">arguments</span>[<span class="number">1</span>]);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>上面的例子可以看出，在JavaScript中，形参不是必须的，传给函数的参数值都可以通过<code>arguments</code>对象获取。</p>
<p>还有很重要的一点，<strong><code>arguments</code>对象的值始终与命名参数（形参）保持同步</strong>。<code>arguments</code>对象的值会自动同步到对应的命名参数，例如修改<code>arguments[1]</code>的值就会同时修改第二个命名参数的值。但是<code>arguments</code>和命名参数在内存中还是分开的，只不过会保持同步而已。</p>
<p>另外还要注意一点：如果只给函数传入了一个参数，那么修改<code>arguments[1]</code>的值并不会同步到第二个命名参数，因为<code>arguments</code>对象的长度是根据传入的参数（实参）决定的，而不是由命名参数（形参）决定的。</p>
<p>最后一点，箭头函数没有<code>arguments</code>对象。</p>
<h1 id="没有重载"><a href="#没有重载" class="headerlink" title="没有重载"></a>没有重载</h1><p>JavaScript中没有函数的重载，后定义的函数会覆盖先定义的函数。但是可以通过检查参数的类型和数量，然后在同一个函数内部执行不同的逻辑。</p>
<h1 id="默认参数值"><a href="#默认参数值" class="headerlink" title="默认参数值"></a>默认参数值</h1><h2 id="为参数设置默认值"><a href="#为参数设置默认值" class="headerlink" title="为参数设置默认值"></a>为参数设置默认值</h2><p>在函数定义的命名参数后面用<code>=</code>显式地赋值，可以为命名参数指定一个默认值：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">makeKing</span>(<span class="params">name = <span class="string">'Henry'</span></span>) {</span><br><span class="line">    <span class="keyword">return</span> <span class="string">`King <span class="subst">${name}</span> VIII`</span>;</span><br><span class="line">}</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">makeKing</span>(<span class="string">'Louis'</span>)); <span class="comment">// 'King Louis VIII'</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="title function_">makeKing</span>()); <span class="comment">// 'King Henry VIII'</span></span><br></pre></td></tr></tbody></table></figure>
<h2 id="默认参数作用域与暂时性死区"><a href="#默认参数作用域与暂时性死区" class="headerlink" title="默认参数作用域与暂时性死区"></a>默认参数作用域与暂时性死区</h2><p>参数是按顺序初始化的，所以后定义默认值的参数可以引用先定义的参数，就像下面这样：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">makeKing</span>(<span class="params">name = <span class="string">'Henry'</span>,numerals = name</span>) {}</span><br></pre></td></tr></tbody></table></figure>
<p>参数初始化顺序遵循<strong>暂时性死区</strong>规则，即前面定义的参数不能引用后面定义的，否则就会抛出错误。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/" class="post-title-link" itemprop="url">计算机网络：网络是怎样连接的</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-07-12 09:07:23" itemprop="dateCreated datePublished" datetime="2023-07-12T09:07:23+08:00">2023-07-12</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-07-24 15:13:28" itemprop="dateModified" datetime="2023-07-24T15:13:28+08:00">2023-07-24</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" itemprop="url" rel="index"><span itemprop="name">-计算机网络</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="一、浏览器生成消息"><a href="#一、浏览器生成消息" class="headerlink" title="一、浏览器生成消息"></a>一、浏览器生成消息</h1><h2 id="1-1-生成HTTP请求消息"><a href="#1-1-生成HTTP请求消息" class="headerlink" title="1.1 生成HTTP请求消息"></a>1.1 生成HTTP请求消息</h2><h3 id="1-1-1-什么是URL"><a href="#1-1-1-什么是URL" class="headerlink" title="1.1.1 什么是URL"></a>1.1.1 什么是URL</h3><p>我们的网络体验从在浏览器中输入网址开始。网址，即URL，URL通过开头不同的标识区分不同的服务，例如访问Web服务器时使用”<a target="_blank" rel="noopener" href="http://&quot;，访问FTP服务器时使用&quot;ftp://&quot;。">http://"，访问FTP服务器时使用"ftp://"。</a></p>
<p>下图列举了几种常用的互联网URL：</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/常见的URL.png" alt="常见的URL"></p>
<p>URL的写法不同，但它们有一个共同点，就是URL开头的文字，即“http:”“ftp:”“file:”“mailto:”这部分文字都表示浏览器应 当使用的访问方法。比如当访问 Web 服务器时应该使用 HTTP协议，而 访问 FTP 服务器时则应该使用 FTP 协议。因此，我们可以把这部分理解为 访问时使用的协议类型。</p>
<h3 id="1-1-2-浏览器解析URL"><a href="#1-1-2-浏览器解析URL" class="headerlink" title="1.1.2 浏览器解析URL"></a>1.1.2 浏览器解析URL</h3><p>浏览器要先解析URL，才能生成发送给Web服务器的消息。下面我们以访问Web服务器为例介绍浏览器解析URL的过程：</p>
<ol>
<li>假设浏览器访问”<a target="_blank" rel="noopener" href="http://www.lovewujunli.com/love/day.html&quot;；">http://www.lovewujunli.com/love/day.html"；</a></li>
<li>浏览器首先对URL进行拆解，得到服务器名称（地址）、文件地址。</li>
<li>浏览器得到：要访问”www.lovewujunli.com”这个服务器上的”love”文件夹下的”dai.html”文件。</li>
</ol>
<h3 id="1-1-3-什么是HTTP协议"><a href="#1-1-3-什么是HTTP协议" class="headerlink" title="1.1.3 什么是HTTP协议"></a>1.1.3 什么是HTTP协议</h3><p>解析完URL之后，浏览器会使用HTTP协议来访问Web服务器，下面介绍一下什么是HTTP协议。</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/HTTP协议.png" alt="HTTP协议"></p>
<p>如上图，HTTP协议定义了客户端和服务器之间交互的消息内容和步骤。首先，客户端向服务器发送请求消息，请求消息包括方法和URI。其中，URI叫作统一资源标识符，简单来说URI标识了要访问的目标。方法表示要让Web服务器完成怎样的操作，下表列举了主要的方法：</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/HTTP的主要方法.png" alt="HTTP的主要方法"></p>
<p>客户端发送的请求消息除了请求方法和URI之外，还有一些用来表示附加信息的<strong>头字段</strong>。客户端向Web服务器发送数据时，会先发送头字段，然后再发送数据，后面会详细介绍头字。</p>
<p>收到请求消息之后，Web 服务器会对其中的内容进行解析，通过 URI 和方法来判断“对什么”“进行怎样的操作”，并根据这些要求来完成自己 的工作，然后将结果存放在响应消息中。在响应消息的开头有一个状态码， 它用来表示操作的执行结果是成功还是发生了错误。</p>
<p>响应消息会被发送回客户端，客户端收到之后，浏览器会从消息中读出所需的数据并显示在屏幕 上。到这里，HTTP 的整个工作就完成了。</p>
<h3 id="1-1-4-生成HTTP请求消息"><a href="#1-1-4-生成HTTP请求消息" class="headerlink" title="1.1.4 生成HTTP请求消息"></a>1.1.4 生成HTTP请求消息</h3><p>对URL进行解析之后浏览器知道要访问的服务器和内容了，接下来就浏览器就要按照规定的格式来生成请求消息了，请求消息的格式如下图所示：</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/HTTP消息的格式.png" alt="HTTP消息的格式"></p>
<p>首先，请求消息的第一行称为请求行。这里的重点是最开头的方法，方法可以告诉 Web 服务器它应该进行怎样的操作。浏览器通过提交请求的方式判断需要用什么方法，比如在地址栏输入URL回车之后浏览器就知道应该用GET方法，如果是在表单处发送请求，浏览器就知道应该用POST方法。</p>
<p>尽管通过第一行我们就可以大致理解请求的内容，但有些情况下还需要一些额外的详细信息，而消息头的功能就是用来存放这些信息。消息头的规格中定义了很多项目，如日期、客户端支持的数据类型、语言、压缩格式、客户端和服务器的软件名称和版本、数据有 效期和最后更新时间等。</p>
<h3 id="1-1-5-发送请求后收到响应"><a href="#1-1-5-发送请求后收到响应" class="headerlink" title="1.1.5 发送请求后收到响应"></a>1.1.5 发送请求后收到响应</h3><p>当浏览器向服务器发送请求后，会接受到服务器发送过来的响应消息，响应消息的格式在上面的图中已经给出了。下表向我们介绍了状态码大概的意思：</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/HTTP状态码概要.png" alt="HTTP状态码概要"></p>
<p>由于每条请求消息中只能写 1 个 URI，所以每次只能获取 1 个文件， 如果需要获取多个文件，必须对每个文件单独发送 1 条请求。比如 1 个网页中包含 3 张图片，那么获取网页加上获取图片，一共需要向 Web 服务器发送 4 条请求。</p>
<p>判断所需的文件，然后获取这些文件并显示在屏幕上，这一系列工作 的整体指挥是浏览器的任务之一，而 Web 服务器却毫不知情。Web 服务器完全不关心这 4 条请求获取的文件到底是 1 个网页上的还是不同网页上 的，它的任务就是对每一条单独的请求返回 1 条响应而已。</p>
<p>到这里，我们已经知道了了浏览器与 Web 服务器进行交互的整个过程。 《网络是怎样连接的》书中第一章最后展示了浏览器与 Web 服务器之间交互消息的一个实例，具体见该书第47页。</p>
<h2 id="1-2-向DNS服务器查询Web服务器的IP地址"><a href="#1-2-向DNS服务器查询Web服务器的IP地址" class="headerlink" title="1.2 向DNS服务器查询Web服务器的IP地址"></a>1.2 向DNS服务器查询Web服务器的IP地址</h2><h3 id="1-2-1-什么是IP地址"><a href="#1-2-1-什么是IP地址" class="headerlink" title="1.2.1 什么是IP地址"></a>1.2.1 什么是IP地址</h3><p>浏览器并不具备发送请求信息的功能，而是通过委托操作系统来发送请求消息，但在这之前要先通过解析URL得到的域名查询服务器的IP地址。在介绍这一操作之前，我们先了解一下什么是IP地址。</p>
<p>互联网和公司内部的局域网都是基于 TCP/IP 的思路来设计的，所以我们先来了解 TCP/IP 的基本思路。TCP/IP 的结构如图 1.8 所示，就是由一些 小的子网，通过路由器连接起来组成一个大的网络。这里的子网可以理解为用集线器连接起来的几台计算机，我们将它看作一个单位，称为子网。 将子网通过路由器连接起来，就形成了一个网络。</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/IP的基本思路.png" alt="IP的基本思路"></p>
<p>在网络中，所有的设备都会被分配一个地址。这个地址就相当于现实中某条路上的“×× 号 ×× 室”。其中“号”对应的号码是分配给整个子网的，而“室”对应的号码是分配给子网中的计算机的，这就是网络中的地址。“号”对应的号码称为网络号，“室”对应的号码称为主机号，这个地址的整体称为 IP 地址。</p>
<p>发送者发出的消息首先经过子网中的集线器，转发到距离发送者最近的路由器上（图 1.8 ①）。接下来，路由器会根据消息的目的地判断下一个路由器的位置，然后将消息发送到下一个路由器，即消息再次经过子网内的集线器被转发到下一个路由器（图 1.8 ②）。前面的过程不断重复，最终消息就被传送到了目的地。</p>
<p>前面这些就是 TCP/IP 中IP地址的基本思路。了解了这些知识之后，让我们再来看一下实际的 IP 地址。如图1.10所示，实际的IP地址是一串32比特的数字，按照8比特（1字节）为一组分成4组，分别用十进制表示然后再用圆点隔开。这就是我们平常经常见到的 IP 地址格式，但仅凭这一串数字我们无法区分哪部分是网络号，哪部分是主机号。在IP地址的规则 中，网络号和主机号连起来总共是32比特，但这两部分的具体结构是不固定的。在组建网络时，用户可以自行决定它们之间的分配关系，因此，我们还需要另外的附加信息来表示 IP 地址的内部结构。</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/IP地址的结构.png" alt="IP地址的结构"></p>
<p>这里的附加信息就是<strong>子网掩码</strong>，子网掩码是一 串与IP地址长度相同的32比特数字，其左边一半都是1，右边一半都是0。其中，子网掩码为1的部分表示网络号，子网掩码为0的部分表示主机号。第一种写法是将子网掩码按照和IP地址一样的方式以每8bit为单位用圆点分组后写在IP地址的右侧，如下图所示。</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/子网掩码第一种写法.png" alt="子网掩码第一种写法"></p>
<p>上面的写法太长，我们还可以把子网掩码中1的部分的位数用十进制表示，并写在IP地址的右侧，比如上图的例子，子网掩码中一共有24位二进制的1，就可以像下图一样简写子网掩码。</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/子网掩码简写法.png" alt="子网掩码简写法"></p>
<p>顺带一提，如下图所示，主机号有两种特别形式，即主机号部分全0或全1。</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/主机号特殊点.png" alt="主机号特殊点"></p>
<h3 id="1-2-2-Socket库提供查询IP地址的功能"><a href="#1-2-2-Socket库提供查询IP地址的功能" class="headerlink" title="1.2.2 Socket库提供查询IP地址的功能"></a>1.2.2 Socket库提供查询IP地址的功能</h3><p>查询 IP 地址的方法非常简单，只要询问最近的DNS服务器“www. lab.glasscom.com的IP地址是什么”就可以了，DNS服务器会回答说“该服务器的 IP 地址为 xxx.xxx.xxx.xxx”。这一过程很简单，那么浏览器是如何向DNS服务器发起查询的呢？让我们先来探索一下DNS。</p>
<p>对于DNS，用户的计算机上有相应的DNS客户端——<strong>DNS解析器</strong>，解析器实际上是一段程序，包含在操作系统的Socket库中，调用的是一个叫作 gethostbyname 的程序组件。Socket库可以让其他的应用程序调用操作系统的网络功能，解析器就是这个库中的一种程序组件。</p>
<p>下面我们来具体了解一下解析器。</p>
<h3 id="1-2-3-通过解析器向DNS服务器发出查询"><a href="#1-2-3-通过解析器向DNS服务器发出查询" class="headerlink" title="1.2.3 通过解析器向DNS服务器发出查询"></a>1.2.3 通过解析器向DNS服务器发出查询</h3><p>调用解析器后，解析器会向DNS服务器发送查询消息，然后DNS服务器会返回响应消息。响应消息中包含查询到的IP地址，解析器会取出IP地址，并将其写入浏览器指定的内存地址中。接 下来，浏览器在向Web服务器发送消息时，只要从该内存地址取出IP地址，并将它与HTTP请求消息一起交给操作系统就可以了。</p>
<h3 id="1-2-4-解析器的原理"><a href="#1-2-4-解析器的原理" class="headerlink" title="1.2.4 解析器的原理"></a>1.2.4 解析器的原理</h3><p> 网络应用程序（这里是浏览器）调用解析器时，程序的控制流程程就会转移到解析器的内部。随后解析器会生成一条要发送给DNS服务器的查询消息，DNS服务器会返回一条响应消息。解析器本身不能发送消息，而是要委托给操作系统内部的协议栈（操作系统内部的网络控制软件，也叫“协议驱动”“TCP/IP 驱动”等）发送查询消息。协议栈会执行发送消息的操作，然后通过网卡将消息发送给DNS服务器。DNS服务器接收到查询消息之后就会返回一条响应消息，包含域名对应的IP地址，这一整个过程称为<strong>域名解析</strong>。</p>
<h2 id="1-3-全世界DNS服务器的大接力"><a href="#1-3-全世界DNS服务器的大接力" class="headerlink" title="1.3 全世界DNS服务器的大接力"></a>1.3 全世界DNS服务器的大接力</h2><h3 id="1-3-1-DNS服务器的基本工作"><a href="#1-3-1-DNS服务器的基本工作" class="headerlink" title="1.3.1 DNS服务器的基本工作"></a>1.3.1 DNS服务器的基本工作</h3><p>来自客户端的查询消息包含下面三个信息：</p>
<ol>
<li>域名：服务器、邮件服务器（邮件地址中 @ 后面的部分）的名称。</li>
<li>Class：在最早设计 DNS 方案时，DNS 在互联网以外的其他网络中的应用也被考虑到了，而 Class 就是用来识别网络的信息。不过，如今除了 互联网并没有其他的网络了，因此 Class 的值永远是代表互联网的 IN。</li>
<li>记录类型：表示域名对应何种类型的记录。例如，当类型为 A 时，表示域名对应的是 IP 地址；当类型为 MX时，表示域名对应的是邮件服务器。对于不同的记录类型，服务器向客户端返回的信息也会不同。</li>
</ol>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/DNS服务器的基本工作.png" alt="DNS服务器的基本工作"></p>
<h3 id="1-3-2-域名的层次结构"><a href="#1-3-2-域名的层次结构" class="headerlink" title="1.3.2 域名的层次结构"></a>1.3.2 域名的层次结构</h3><p>DNS 服务器中的所有信息都是按照域名以分层次的结构来保存的。DNS中的域名都是用句点来分隔的，比如www.lab.glasscom.com，这里的句点代表了不同层次之间的界限，在域名中，越靠右的位置表示其层级越高。其中，相当于一个层级的部分称为<strong>域</strong>。例如前面的域名，com域的下一层是glasscom域，再下一层是lab域，再下面才是www这个名字。</p>
<p>于是，DNS服务器也具有了像域名一样的层次结构，每个域的信息都存放在相应层级的DNS服务器中。</p>
<p>再补充一点，在一个域下面可以创建<strong>下级域</strong>，比如，假设某公司的域为 example.cn，我们可以在这个域的下面创建两个子域,例如sub1. example.cn 和 sub2.example.cn，然后就可以将这两个下级域分配给公司不同的部门来使用。</p>
<h3 id="1-3-3-寻找相应的DNS服务器并获取IP地址"><a href="#1-3-3-寻找相应的DNS服务器并获取IP地址" class="headerlink" title="1.3.3 寻找相应的DNS服务器并获取IP地址"></a>1.3.3 寻找相应的DNS服务器并获取IP地址</h3><p>从DNS服务器获取域名对应的IP地址的关键是如何找到我们要访问的Web服务器信息归哪一台DNS服务器管。</p>
<p>首先，将负责管理下级域的DNS服务器的IP地址注册到它们的上级DNS服务器中，然后上级 DNS 服务器的IP地址再注册到更上一级的 DNS 服务器中，以此类推。这样，我们就可以通过上级DNS服务器查询出下级DNS服务器的IP地址，也就可以向下级 DNS 服务器发送查询请求了。</p>
<p>在互联网中，com和cn的上面还有一级域，称为根域，书写域名时常被省略。根域的DNS服务器中保管着com、cn等的 DNS 服务器的信息。根域的DNS服务器信息保存在互联网中所有的DNS服务器中。这样一来，任何DNS服务器就都可以找到并访问根域DNS服务器了。因此，客户端只要能够找到任意一台DNS服务器，就可以通过它找到根域DNS服务器，然后再一路顺藤摸瓜找到位于下层的某台目标 DNS 服务器。这个过程如下图所示。</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/寻找DNS服务器过程.png" alt="寻找DNS服务器的过程"></p>
<h3 id="1-3-4-通过缓存加快DNS服务器的响应"><a href="#1-3-4-通过缓存加快DNS服务器的响应" class="headerlink" title="1.3.4 通过缓存加快DNS服务器的响应"></a>1.3.4 通过缓存加快DNS服务器的响应</h3><p>有时候并不需要从最上级的根域开始查找，因为 DNS 服务器有一 个缓存功能，可以记住之前查询过的域名。如果要查询的域名和相关信息已经在缓存中，那么就可以直接返回响应，接下来的查询可以从缓存的位置开始向下进行。相比每次都从根域找起来说，缓存可以减少查询所需的时间。</p>
<p>并且，当要查询的域名不存在时，“不存在”这一响应结果也会被缓存。这样，当下次查询这个不存在的域名时，也可以快速响应。</p>
<p>这个缓存机制中有一点需要注意，那就是信息被缓存后，原本的注册信息可能会发生改变，这时缓存中的信息就有可能是不正确的。因此，DNS服务器中保存的信息都设置有一个有效期，当缓存中的信息超过有效期后，数据就会从缓存中删除。</p>
<h2 id="1-4-委托协议栈发送消息"><a href="#1-4-委托协议栈发送消息" class="headerlink" title="1.4 委托协议栈发送消息"></a>1.4 委托协议栈发送消息</h2><h3 id="1-4-1-数据收发操作概览"><a href="#1-4-1-数据收发操作概览" class="headerlink" title="1.4.1 数据收发操作概览"></a>1.4.1 数据收发操作概览</h3><p>知道了 IP 地址之后，就可以委托操作系统内部的协议栈向这个目标 IP 地址，也就是我们要访问的 Web 服务器发送消息了。这里需要按照指定的顺序调用多个程序组件，这个过程有点复杂，下图简要概述这个过程。</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/数据收发过程.png" alt="数据收发过程"></p>
<p>进行收发数据操作之前，双方需要先建立起收发数据的管道才行。建立管道的关键在于管道两端的数据出入口，这些出入口称为<strong>套接字</strong>。</p>
<p>首先，服务器一方先创建套接字，然后等待客户端向该套接字连接管道 。当服务器进入等待状态时，客户端就可以连接管道了。具体来说，客户端也会先创建一个套接字，当双方的套接字连接起来之后，通信准备就完成了。接下来，就像我们刚刚讲过的一样，只要将数据送入套接字就可以收发数据了。</p>
<p>当数据全部发送完毕之后，连接的管道将会被断开。管道在连接时是由客户端发起的，但在断开时可以由客户端或服务器任意一方发起。其中一方断开后，另一方也会随之断开，当管道断开后，套接字也会被删除。到此为止，通信操作就结束了。</p>
<p>综上所述，收发数据的操作分为若干个阶段，可以大致总结为以下 4 个：</p>
<ol>
<li>创建套接字（创建套接字阶段） 。</li>
<li>将管道连接到服务器端的套接字上（连接阶段） 。</li>
<li>收发数据（通信阶段） 。</li>
<li>断开管道并删除套接字（断开阶段）</li>
</ol>
<p>这4个操作都是由操作系统中的协议栈来执行的，浏览器等应用程序并不会自己去做连接管道、放入数据这些工作，而是委托协议栈来代劳。</p>
<p>下面分阶段来介绍一下收发数据的具体过程。</p>
<h3 id="1-4-2-创建套接字阶段"><a href="#1-4-2-创建套接字阶段" class="headerlink" title="1.4.2 创建套接字阶段"></a>1.4.2 创建套接字阶段</h3><p>应用程序（浏览器）委托收发数据过程的关键点就是像对 DNS 服务器发送查询一样，调用 Socket 库中的socket 程序组件。</p>
<p>套接字创建完成后，协议栈会返回一个描述符，应用程序会将收到的描述符存放在内存中。描述符是用来识别计算机内协议栈中不同的套接字的，不是用来识别另一方的套接字。比如浏览器打开多个网页，就会在客户端协议栈内创建多个套接字，这里的描述符就是用来识别客户端协议栈内多个套接字的。只要我们出示描述符， 协议栈就能够判断出我们希望用哪一个套接字来连接或者收发数据了。</p>
<h3 id="1-4-3-连接阶段"><a href="#1-4-3-连接阶段" class="headerlink" title="1.4.3 连接阶段"></a>1.4.3 连接阶段</h3><p>接下来，我们需要委托协议栈将客户端创建的套接字与服务器那边的套接字连接起来。应用程序通过调用Socket库中的connect的程序组件来完成这一操作。当调用 connect 时，需要指定描述符、 服务器 IP 地址和端口号这 3 个参数，下面介绍一下这三个参数的作用。</p>
<ol>
<li><strong>描述符</strong>：就是在创建套接字的时候由协议栈返回的那 个描述符。connect 会将应用程序指定的描述符告知协议栈，然后协议栈根据这个描述符来判断到底使用哪一个套接字去和服务器端的套接字进行连接，并执行连接的操作。</li>
<li><strong>服务器 IP 地址</strong>，就是通过 DNS 服务器查询得到的我们要访问的服务器的 IP 地址。在 DNS 服务器的部分已经讲过，在进行数据收发操作时，双方必须知道对方的 IP 地址并告知协议栈。</li>
<li><strong>端口号</strong>：描述符是用来在一台机器中选择协议栈某一个套接字进行连接操作的，端口号则是用来在服务器中识别具体的套接字的，IP地址用来连接到某个服务器，端口号则是具体选择对应服务器的某个套接字。服务器上所使用的端口号是根据应用的种类事先规定好的，比如 Web 是 80 号端口，电子邮件是 25 号端口。</li>
</ol>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/描述符与端口号的区别.png" alt="描述符与端口号的区别"></p>
<p>当连接成功后，协议栈会将对方的 IP 地址和端口号等信息保存在套接字中，这样我们就可以开始收发数据了。</p>
<h3 id="1-4-4-通信阶段：传递消息"><a href="#1-4-4-通信阶段：传递消息" class="headerlink" title="1.4.4 通信阶段：传递消息"></a>1.4.4 通信阶段：传递消息</h3><p>当套接字连接起来之后，剩下的事情就简单了。只要将数据送入套接字，数据就会被发送到对方的套接字中。这个操作需 要使用Socket库中write这个程序组件，具体过程如下。</p>
<p>首先，应用程序需要在内存中准备好要发送的数据。根据用户输入的网址生成的 HTTP 请求消息就是我们要发送的数据。接下来，当调用 write 时，需要指定描述符和发送数据，然后协议栈就会将数据发送到服务器。由于在完成上一个连接阶段之后，套接字中已经保存了已连接的通信对象的相关信息（包括端口号），所以只要通过描述符指定套接字，就可以识别出通信对象，并向其发送数据。 接着，发送数据会通过网络到达我们要访问的服务器。</p>
<p>接下来，服务器执行接收操作，解析收到的数据内容并执行相应的操作，向客户端返回响应消息。</p>
<p>当消息返回后，需要执行的是接收消息的操作。接收消息的操作是通过 Socket 库中的 read 程序组件委托协议栈来完成的。调用 read 时需要指定用于存放接收到的响应消息的内存地址，这一内存地址称为<strong>接收缓冲区</strong>。接收缓冲区是一块位于应用程序内部的内存空间，因此当消息被存放到接收缓冲区中时，就相当于已经转交给了应用程序。</p>
<h3 id="1-4-5-断开阶段：收发数据结束"><a href="#1-4-5-断开阶段：收发数据结束" class="headerlink" title="1.4.5 断开阶段：收发数据结束"></a>1.4.5 断开阶段：收发数据结束</h3><p>当浏览器收到数据之后，收发数据的过程就结束了。接下来，我们需要调用 Socket 库的 close 程序组件进入断开阶段，之后协议栈内对应的套接字会被删除，数据通路（前面说的管道）也随之消失。</p>
<p>断开的过程如下。Web 使用的 HTTP 协议规定，当 Web 服务器发送完响应消息之后，应该主动执行断开操作 ，因此 Web 服务器会首先调用 close 来断开连接。断开操作传达到客户端之后，客户端的套接字也会进入 断开阶段。接下来，当浏览器调用 read 执行接收数据操作时，read 会告知浏览器收发数据操作已结束，连接已经断开。浏览器得知后，也会调用close 进入断开阶段。</p>
<p>要注意的是，根据应用种类不同，客户端和服务器哪一方先执行close都有可能。有些应用中是客户端先执行close。</p>
<p>这就是 HTTP 的工作过程。HTTP 协议将 HTML 文档和图片都作为单独的对象来处理，每获取一次数据，就要执行一次连接、发送请求消息、 接收响应消息、断开的过程。因此，如果一个网页中包含很多张图片，就必须重复进行很多次连接、收发数据、断开的操作。</p>
<h2 id="第一章结语"><a href="#第一章结语" class="headerlink" title="第一章结语"></a>第一章结语</h2><p>到这里第一章就结束了，本章了解了浏览器与 Web 服务器之间收发消息的过程，但实际负责收发消息的是协议栈、网卡驱动和网卡，只有这 3 者相互配合，数据才能够在网络中流动起来。下一章将对这一部分进行探索。</p>
<h1 id="二、协议栈和网卡"><a href="#二、协议栈和网卡" class="headerlink" title="二、协议栈和网卡"></a>二、协议栈和网卡</h1><h2 id="2-1-创建套接字"><a href="#2-1-创建套接字" class="headerlink" title="2.1 创建套接字"></a>2.1 创建套接字</h2><h3 id="2-1-1-协议栈的内部结构"><a href="#2-1-1-协议栈的内部结构" class="headerlink" title="2.1.1 协议栈的内部结构"></a>2.1.1 协议栈的内部结构</h3><p>协议栈的结构如下所示，分为不同部分，并且执行流是从上到下的：</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/协议栈的结构.png" alt="协议栈的结构"></p>
<p>最上面的部分是网络应用程序，也就是浏览器、电子邮件客户端、Web 服务器、电子邮件服务器等程序，它们会将收发数据等工作委派给下层的部分来完成。</p>
<p>应用程序的下面是 Socket 库，其中包括解析器，解析器用来向 DNS 服务器发出查询。再下面就是操作系统内部了，其中包括协议栈。协议栈的上半部分有两块，分别是负责用 TCP 协议收发数据的部分和负责用 UDP 协议收发数据的部分。</p>
<blockquote>
<p>浏览器、邮件等一般应用程序收发数据时用 TCP； </p>
<p>DNS 查询等收发较短的控制数据时用 UDP。</p>
</blockquote>
<p>下面一半是用 IP 协议控制网络包收发操作的部分。在互联网上传送数据时，数据会被切分成一个一个的网络包 ，而将网络包发送给通信对象的工作就是由IP模块完成的。IP 中还包括 ICMPA 协议和 ARPB 协议。 ICMP 用于告知网络包传送过程中产生的错误以及各种控制消息，ARP 用于根据 IP 地址查询相应的以太网 MAC 地址。</p>
<p> IP 下面的网卡驱动程序负责控制网卡硬件，而最下面的网卡则负责完成实际的收发操作，也就是对网线中的信号执行发送和接收的操作</p>
<h3 id="2-1-2-套接字的实体就是通道控制信息"><a href="#2-1-2-套接字的实体就是通道控制信息" class="headerlink" title="2.1.2 套接字的实体就是通道控制信息"></a>2.1.2 套接字的实体就是通道控制信息</h3><p>在协议栈内部有一块用于存放控制信息的内存空间，这里记录了用于控制通信操作的控制信息，例如通信对象的 IP 地址、端口号、通信操作的进行状态等。<strong>套接字的实体就是协议栈内部存放通道控制信息</strong>。</p>
<p>协议栈在执行操作时需要参阅这些控制信息。例如，在发送数据时，需要看一看套接字中的通信对象 IP 地址和端口号，在发送数据时套接字中还必须要记录是否已经收到响应，以及发送数据后经过了多长时间，根据这些信息在可以在数据发送失败后重新发送。</p>
<p>综上所述，协议栈是根据套接字中记录的控制信息来工作的。</p>
<h3 id="2-1-3-创建套接字的具体过程"><a href="#2-1-3-创建套接字的具体过程" class="headerlink" title="2.1.3 创建套接字的具体过程"></a>2.1.3 创建套接字的具体过程</h3><p>应用程序调用 socket 申请创建套接字，协议栈首先会分配用于存放一个套接字所需的内存空间，用于记录套接字控制信息。套接字刚刚创建时，数据收发操作还没有开始，因此需要在套接字的内存空间中写入表示这一初始状态的控制信息。到这里，创建套接字的操作就完成了。</p>
<p>简单来说，创建套接字时，首先分配一个套接字所需的内存空间，然后向其中写入初始状态。</p>
<h2 id="2-2-连接服务器"><a href="#2-2-连接服务器" class="headerlink" title="2.2 连接服务器"></a>2.2 连接服务器</h2><h3 id="2-2-1-连接阶段做的事儿"><a href="#2-2-1-连接阶段做的事儿" class="headerlink" title="2.2.1 连接阶段做的事儿"></a>2.2.1 连接阶段做的事儿</h3><p>这里的连接并不是我们所认为的网络的连接，因为网络通过网线一直都是处于连接状态的，这里的<strong>连接实际上是通信双方交换控制信息</strong>，在套接字中记录这些必要信息并准备数据收发的一连串操作。</p>
<p>套接字刚创建完成时里面没有任何数据，所以它不知道通信的对象是谁，所以协议栈也不知道要给谁发送数据。但浏览器可以通过解析URL查询到服务器的IP地址，同时也可以知道要使用的端口号。<strong>连接的第一步操作便是浏览器将通信对象的具体信息通过套接字告诉协议栈</strong>。</p>
<p>服务器上的套接字情况是一样的，也不知道通信对象的具体信息。和客户端不一样，服务器的应用程序也不知道通信对象（即客户端）的具体信息。于是<strong>连接的另一个目的就是让客户端告诉服务器关于客户端的具体信息</strong>，包括客户端IP地址与端口号等。</p>
<p>上面提到的<strong>控制信息就是在收发数据时所需的一些信息</strong>，比如IP地址和端口号，除此之外还有一些其他的控制信息。连接操作中交换的控制信息是根据通信规则来确定的，也就是说使用不同的通信规则所需的控制信息也不同。</p>
<p>此外，在执行数据收发操作时，我们需要一块用来临时存放要收发的数据的内存空间，这块内存空间称为缓冲区，这也是在连接的阶段进行的。</p>
<p>综上，我们可以总结一下连接阶段具体做了哪些事儿：</p>
<ol>
<li>浏览器将通信对象的信息放入套接字。</li>
<li>告诉服务器关于客户端的信息。</li>
<li>在客户端开辟<strong>缓冲区</strong>，用来临时存放要收发的数据。</li>
</ol>
<h3 id="2-2-2-负责保存控制信息的头部"><a href="#2-2-2-负责保存控制信息的头部" class="headerlink" title="2.2.2 负责保存控制信息的头部"></a>2.2.2 负责保存控制信息的头部</h3><p>我们上面说的控制信息可以分为两类：一类是客户端和服务器相互联络时交换的控制信息，另一类是保存在套接字中用来控制协议栈操作数据的控制信息。</p>
<p>我们先来看看客户端和服务器相互联络时交换的控制信息，<strong>这些信息在整个通信阶段都需要</strong>。这些信息位于网络包的开头，所以又称为<strong>头部</strong>。此外，以太网和 IP 协议也有自己的控制信息，这些信息也叫头部，为了避免各种不同的头部发生混淆，我们一般会记作<strong>TCP头部</strong>、<strong>以太网头部</strong> 、<strong>IP</strong>头部。</p>
<p>客户端和服务器在通信中会将必要的信息记录在头部并相互确认，例如下面这样：</p>
<blockquote>
<p>发送方：“开始数据发送。” </p>
<p>接收方：“请继续。” </p>
<p>发送方：“现在发送的是 ×× 号数据。” </p>
<p>接收方：“×× 号数据已收到。” </p>
<p>……（以下省略）</p>
</blockquote>
<p>我们上面所说的是<strong>TCP协议</strong>，下面我们来看看它的具体格式：</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/TCP头部格式.png" alt="TCP头部格式"></p>
<p>下面我们再来看看保存在套接字中用来控制协议栈操作数据的控制信息，这些信息保存在协议栈中的套接字内存空间中。应用程序传过来的数据以及从通信对象接收到的数据都会保存在这里。套接字的控制信息和协议栈的程序本身其实是一体的，即协议栈中的控制信息通信对方是看不见的，但只要在通信时按照规则将必要的信息写入头部，客户端和服务器之间的通信就能够得以成立。</p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/客户端与服务器之间交换的控制信息.png" alt="客户端与服务器之间交换的控制信息"></p>
<p><img src="/2023/07/12/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%EF%BC%9A%E7%BD%91%E7%BB%9C%E6%98%AF%E6%80%8E%E6%A0%B7%E8%BF%9E%E6%8E%A5%E7%9A%84/控制信息分类.png" alt="控制信息分类"></p>
<h3 id="2-2-3-连接操作的实际过程"><a href="#2-2-3-连接操作的实际过程" class="headerlink" title="2.2.3 连接操作的实际过程"></a>2.2.3 连接操作的实际过程</h3><p>我们已经知道，连接是从应用程序调用 Socket 库的 connect 开始的，这个调用会将服务器的IP地址传递给协议栈中的TCP模块。然后TCP模块会与该IP地址对应的通信对象的TCP模块交换控制信息，下面我们探讨一下交互的具体流程。</p>
<p>首先，客户端创建一个包含表示开始进行数据收发操作的控制信息的头部，从表2.1的TCP头部格式我们可以知道，头部里包含发送方和接收方的端口号，到这里客户端（发送方）就找到了服务器（接收方）的套接字</p>
<p>然后，将头部中的控制位的 SYN 比特设置为 1，现在可以认为它表示连接，后面会详细讲解。此外还需要设置适当的序号和窗口大小，这一点也会在后面详细讲解。</p>
<p>上面两段我们可以知道，连接操作的第一步是在 TCP 模块处创建表示连接控制信息的头部。然后通过 TCP 头部中的发送方和接收方端口号可以找到要连接的套接字。</p>
<p>TCP头部创建好之后，TCP模块会将信息传递给IP模块委托它来发送，IP模块会将网络包通过网络发送给服务器。然后服务器上的IP模块会将接收到的数据传递给TCP模块，然后服务器的 TCP 模块根据 TCP 头部中的信息找到端口号对应的套接字。当找到对应的套接字之后，套接字中会写入相应的信息，并将状态改为正在连接。</p>
<p>上述操作完成后，服务器的 TCP 模块会返回响应，这个过程和客户端一样，需要在 TCP 头部中设置发送方和接收方端口号以及 SYN 比特。如果由于某些原因不接受连接，那么将不设置 SYN，而是将 RST 比特设 置为 1。</p>
<p>在返回响应时还需要将 ACK 控制位设为 1，这表示已经接收到相应的网络包。网络中经常会发生错误，网络包也会发生丢失，因此双方在通信时必须相互确认网络包是否已经送达 ，而设置 ACK 比特就是用来进行这一确认的。接下来，服务器 TCP 模块会将 TCP 头部传递给 IP 模块，并委托 IP 模块向客户端返回响应。</p>
<p>然后，网络包就会返回到客户端，通过 IP 模块到达 TCP 模块，并通过 TCP 头部的信息确认连接服务器的操作是否成功。如果 SYN 为 1 则表示连接成功，这时会向套接字中写入服务器的 IP 地址、端口号等信息，同时还会将状态改为连接完毕，最后客户端也需要将 ACK 比特设置为 1 并发回服务器，告诉服务器刚才的响应包已经收到。当这个服务器收到这个返回包之后，连接操作才算全部完成。</p>
<p>现在，套接字就已经进入随时可以收发数据的状态了。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/07/10/%E5%8F%98%E9%87%8F%E3%80%81%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/07/10/%E5%8F%98%E9%87%8F%E3%80%81%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/" class="post-title-link" itemprop="url">未命名</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-07-10 16:06:33" itemprop="dateCreated datePublished" datetime="2023-07-10T16:06:33+08:00">2023-07-10</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-07-12 11:14:35" itemprop="dateModified" datetime="2023-07-12T11:14:35+08:00">2023-07-12</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="复制值"><a href="#复制值" class="headerlink" title="复制值"></a>复制值</h1><p>原始值变量的复制十分简单，首先会创建一个原变量的副本，然后将原变量的值赋给副本变量：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> num1 = <span class="number">5</span>;</span><br><span class="line"><span class="keyword">let</span> num2 = num1;</span><br></pre></td></tr></tbody></table></figure>
<p>引用值的复制则是对指针的复制，也就是说这里复制的值实际上是一个指针，它指向存储在堆内存中的对象。复制完成后，两个变量其实是指向同一个对象，因此修改其中一个对象，会映射到另一个对象中。看下面的例子：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj1 = <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="keyword">let</span> obj2 = obj1;</span><br><span class="line">obj1.<span class="property">name</span> = <span class="string">"JunLi"</span>;</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj2.<span class="property">name</span>); <span class="comment">// "JunLi"</span></span><br></pre></td></tr></tbody></table></figure>
<p>上面的例子中<code>obj1</code>与<code>obj2</code>都指向同一个对象。</p>
<h1 id="传递参数（深入理解按值传参）"><a href="#传递参数（深入理解按值传参）" class="headerlink" title="传递参数（深入理解按值传参）"></a>传递参数（深入理解按值传参）</h1><p>根据红宝书，JavaScript中传参明确是<strong>只有按值传参</strong>这一种方式。</p>
<p>按值传参时，值会被复制到一个局部变量（即形参）。使用原始值传参时很明显能体现按值传参的特点，这里着重讨论引用类型变量的传参。</p>
<p>红宝书首先给了一个例子：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">setName</span>(<span class="params">obj</span>) {</span><br><span class="line">    obj.<span class="property">name</span> = <span class="string">"JunLi"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="title function_">setName</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>); <span class="comment">// "JunLi"</span></span><br></pre></td></tr></tbody></table></figure>
<p>上面的例子中，在<code>setName()</code>函数内部对<code>obj</code>对象属性的修改映射到了函数外部的<code>person</code>对象，这很容易让我们认为是引用传参，实则不然，红宝书紧接着又给了下面的例子：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">setName</span>(<span class="params">obj</span>) {</span><br><span class="line">    obj.<span class="property">name</span> = <span class="string">"JunLi"</span>;</span><br><span class="line">    obj = <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    obj.<span class="property">name</span> = <span class="string">"Fei"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">person.<span class="title function_">setName</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>); <span class="comment">// JunLi</span></span><br></pre></td></tr></tbody></table></figure>
<p>如果是按引用传参的话，上面的例子最后输出的应该就是<code>Fei</code>，因为在将<code>obj</code>的<code>name</code>属性设为<code>JunLi</code>之后将<code>obj</code>重新初始化为一个新对象，并给这个新对象的<code>name</code>属性赋值为<code>Fei</code>。那么理所当然应该映射到函数外部的<code>person</code>对象，<code>person</code>变量应该也初始化为新变量，并有一个值为<code>Fei</code>的<code>name</code>属性。然而输出的还是<code>JunLi</code>，这里是因为使用的还是按值传参，也可以更进一步传过去的是<code>person</code>对象的引用的副本，所以在函数内部的<code>obj.name = "Fei"</code>语句只是重写了<code>obj</code>，指向了新的对象，而不影响函数外部原来的引用。</p>
<h1 id="执行上下文与作用域"><a href="#执行上下文与作用域" class="headerlink" title="执行上下文与作用域"></a>执行上下文与作用域</h1><h2 id="执行上下文"><a href="#执行上下文" class="headerlink" title="执行上下文"></a>执行上下文</h2><p>先来看下什么是执行上线文？</p>
<blockquote>
<p>当代码运行时，会产生一个对应的执行环境，在这个环境中，所有变量会被事先提出来（变量提升），有的直接赋值，有的为默认值 undefined，代码从上往下开始执行，就叫做执行上下文。</p>
</blockquote>
<p>对于每个执行上下文，都有三个重要属性：</p>
<ul>
<li>变量对象(Variable object，VO)</li>
<li>作用域链(Scope chain)</li>
<li>this</li>
</ul>
<p>执行上下文可分为为：<strong>全局执行上下文</strong> 和 <strong>函数执行上下文。</strong></p>
<p>在执行全局代码前，将window定义为「<strong>全局执行上下文</strong>」，即全局上下文就是window对象。</p>
<p>在调用函数准备执行函数之前，创建对应的「<strong>函数执行上下文</strong>」</p>
<p>对<strong>全局数据</strong>进通常进行如下预处理，处理完后开始执行全局代码。</p>
<ol>
<li>var定义的全局变量==&gt; undefined，添加为window的属性</li>
<li>function 声明的全局函数==&gt;赋值(fun)，添加为window的方法</li>
<li>this ==&gt;赋值(window)</li>
</ol>
<p>看下面的例子：</p>
<p><img src="https://pic2.zhimg.com/80/v2-c36942289231aaa08281d1bcc8f40e35_720w.webp" alt="img"></p>
<p>对<strong>函数执行上下文</strong>数据进通常进行如下预处理，处理完后开始函数代码，函数执行完之后，内部的局部变量会被释放。</p>
<ol>
<li>形参变量—&gt; 赋值(实参)—&gt; 添加为执行上下文的属性</li>
<li>arguments—&gt; 赋值(实参列表)，添加为执行上下文的属性</li>
<li>var定义的局部变量—&gt; undefined，添加为执行上下文的属性</li>
<li>function 声明的函数—&gt;赋值(fun)，添加为执行上下文的方法</li>
<li>this —&gt;赋值(调用函数的对象)</li>
</ol>
<h2 id="深入执行上下文的活动对象"><a href="#深入执行上下文的活动对象" class="headerlink" title="深入执行上下文的活动对象"></a>深入执行上下文的活动对象</h2><h3 id="什么是活动对象"><a href="#什么是活动对象" class="headerlink" title="什么是活动对象"></a>什么是活动对象</h3><p>我们都知道，全局上下文有一个全局上下文对象，也就是window对象。用var定义的全局变量和用function定义的函数都会成为window对象的属性和方法，下面我们具体谈谈函数执行上下文的变量对象。</p>
<p>在函数上下文中，我们用活动对象(activation object, AO)来表示变量对象。</p>
<p>活动对象和变量对象其实是一个东西，只是变量对象是规范上的或者说是引擎实现上的，不可在 JavaScript 环境中访问，只有到当进入一个执行上下文中，这个执行上下文的变量对象才会被激活，而只有被激活的变量对象，也就是活动对象上的各种属性才能被访问。</p>
<p>活动对象是在进入函数上下文时刻被创建的，它通过函数的 arguments 属性初始化。arguments 属性值是 Arguments 对象。</p>
<h3 id="执行过程"><a href="#执行过程" class="headerlink" title="执行过程"></a>执行过程</h3><p>执行上下文的代码会分成两个阶段进行处理：分析和执行，我们也可以叫做：</p>
<ol>
<li>进入执行上下文</li>
<li>代码执行</li>
</ol>
<h3 id="进入执行上下文"><a href="#进入执行上下文" class="headerlink" title="进入执行上下文"></a>进入执行上下文</h3><p>当进入执行上下文时，这时候还没有执行代码，</p>
<p>变量对象会包括：</p>
<ol>
<li>函数的所有形参 (如果是函数上下文)<ul>
<li>由名称和对应值组成的一个变量对象的属性被创建</li>
<li>没有实参，属性值设为 undefined</li>
</ul>
</li>
<li>函数声明<ul>
<li>由名称和对应值（函数对象(function-object)）组成一个变量对象的属性被创建</li>
<li>如果变量对象已经存在相同名称的属性，则完全替换这个属性</li>
</ul>
</li>
<li>变量声明<ul>
<li>由名称和对应值（undefined）组成一个变量对象的属性被创建；</li>
<li>如果变量名称跟已经声明的形式参数或函数相同，则变量声明不会干扰已经存在的这类属性</li>
</ul>
</li>
</ol>
<p>看下面的例子：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">foo</span>(<span class="params">a</span>) {</span><br><span class="line">  <span class="keyword">var</span> b = <span class="number">2</span>;</span><br><span class="line">  <span class="keyword">function</span> <span class="title function_">c</span>(<span class="params"></span>) {}</span><br><span class="line">  <span class="keyword">var</span> d = <span class="keyword">function</span>(<span class="params"></span>) {};</span><br><span class="line"></span><br><span class="line">  b = <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">foo</span>(<span class="number">1</span>);</span><br></pre></td></tr></tbody></table></figure>
<p>在进入执行上下文后，这时候的 AO 是：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable constant_">AO</span> = {</span><br><span class="line">    <span class="attr">arguments</span>: {</span><br><span class="line">        <span class="number">0</span>: <span class="number">1</span>,</span><br><span class="line">        <span class="attr">length</span>: <span class="number">1</span></span><br><span class="line">    },</span><br><span class="line">    <span class="attr">a</span>: <span class="number">1</span>,</span><br><span class="line">    <span class="attr">b</span>: <span class="literal">undefined</span>,</span><br><span class="line">    <span class="attr">c</span>: reference to <span class="keyword">function</span> <span class="title function_">c</span>(<span class="params"></span>){},</span><br><span class="line">    <span class="attr">d</span>: <span class="literal">undefined</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>在代码执行阶段，会顺序执行代码，根据代码，修改变量对象的值</p>
<p>还是上面的例子，当代码执行完后，这时候的 AO 是：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable constant_">AO</span> = {</span><br><span class="line">    <span class="attr">arguments</span>: {</span><br><span class="line">        <span class="number">0</span>: <span class="number">1</span>,</span><br><span class="line">        <span class="attr">length</span>: <span class="number">1</span></span><br><span class="line">    },</span><br><span class="line">    <span class="attr">a</span>: <span class="number">1</span>,</span><br><span class="line">    <span class="attr">b</span>: <span class="number">3</span>,</span><br><span class="line">    <span class="attr">c</span>: reference to <span class="keyword">function</span> <span class="title function_">c</span>(<span class="params"></span>){},</span><br><span class="line">    <span class="attr">d</span>: reference to <span class="title class_">FunctionExpression</span> <span class="string">"d"</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h3 id="变量对象总结"><a href="#变量对象总结" class="headerlink" title="变量对象总结"></a>变量对象总结</h3><p>到这里变量对象的创建过程就介绍完了，让我们简洁的总结我们上述所说：</p>
<ol>
<li>全局上下文的变量对象初始化是全局对象</li>
<li>函数上下文的变量对象初始化只包括 Arguments 对象</li>
<li>在进入执行上下文时会给变量对象添加形参、函数声明、变量声明等初始的属性值</li>
<li>在代码执行阶段，会再次修改变量对象的属性值</li>
</ol>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/07/02/JS%EF%BC%9A%E9%9B%86%E5%90%88%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/07/02/JS%EF%BC%9A%E9%9B%86%E5%90%88%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B/" class="post-title-link" itemprop="url">JS：集合引用类型</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-07-02 15:02:17" itemprop="dateCreated datePublished" datetime="2023-07-02T15:02:17+08:00">2023-07-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-07-06 23:32:43" itemprop="dateModified" datetime="2023-07-06T23:32:43+08:00">2023-07-06</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%89%8D%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-前端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="Object"><a href="#Object" class="headerlink" title="Object"></a>Object</h1><p><code>Object</code>适合存储和在应用之间交换数据。显式地创建<code>Object</code>类型有两种方式，一种是使用<code>new</code>操作符，另一种是使用<code>对象字面量</code>的方式创建<code>Object</code>。</p>
<h2 id="使用new操作符创Object"><a href="#使用new操作符创Object" class="headerlink" title="使用new操作符创Object"></a>使用<code>new</code>操作符创<code>Object</code></h2><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">person.<span class="property">name</span> = <span class="string">"吴大孬"</span>;</span><br><span class="line">person.<span class="property">age</span> = <span class="number">18</span>;</span><br></pre></td></tr></tbody></table></figure>
<h2 id="使用对象字面量创建Object"><a href="#使用对象字面量创建Object" class="headerlink" title="使用对象字面量创建Object"></a>使用对象字面量创建<code>Object</code></h2><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> person = {</span><br><span class="line">    <span class="attr">name</span>: <span class="string">"吴大孬"</span>,</span><br><span class="line">    <span class="attr">age</span>: <span class="number">18</span></span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure>
<p>也可以先创建一个空的<code>Object</code>，随后添加属性与值：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> person = {};</span><br><span class="line">person.<span class="property">name</span> = <span class="string">"吴大孬"</span>;</span><br><span class="line">person.<span class="property">age</span> = <span class="number">18</span>;</span><br></pre></td></tr></tbody></table></figure>
<p>使用对象字面量创建对象时要注意以下几点：</p>
<ul>
<li>属性之间用逗号隔开，最后一个属性不要加逗号。</li>
<li>属性名可以是字符串或数值。</li>
<li>使用对象字面量创<code>Object</code>时，不会调用<code>Object</code>构造函数。</li>
</ul>
<h2 id="使用Object为方法传递大量可选参数"><a href="#使用Object为方法传递大量可选参数" class="headerlink" title="使用Object为方法传递大量可选参数"></a>使用Object为方法传递大量可选参数</h2><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">displayInfo</span>(<span class="params">args</span>) {</span><br><span class="line">    <span class="keyword">let</span> output = <span class="string">""</span>;</span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">typeof</span> args.<span class="property">name</span> == <span class="string">"string"</span>) {</span><br><span class="line">        output += <span class="string">"Name: "</span> + args.<span class="property">name</span> + <span class="string">"\n"</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">typeof</span> args.<span class="property">age</span> == <span class="string">"number"</span>) {</span><br><span class="line">        output += <span class="string">"Age: "</span> + args.<span class="property">age</span> + <span class="string">"\n"</span>;</span><br><span class="line">    }</span><br><span class="line">    <span class="title function_">alert</span>(output);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">displayInfo</span>({</span><br><span class="line">    <span class="attr">name</span>: <span class="string">"吴大孬"</span>,</span><br><span class="line">    <span class="attr">age</span>: <span class="number">18</span></span><br><span class="line">});</span><br></pre></td></tr></tbody></table></figure>
<h2 id="存取Object属性"><a href="#存取Object属性" class="headerlink" title="存取Object属性"></a>存取<code>Object</code>属性</h2><p>最常用的是通过<strong>点语法</strong>存取<code>Object</code>的属性，也可以通过中括号来存取<code>Object</code>属性，中括号内使用属性名的字符串形式，例如：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person[<span class="string">"name"</span>]);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person[<span class="string">"first name"</span>])</span><br></pre></td></tr></tbody></table></figure>
<h1 id="Array"><a href="#Array" class="headerlink" title="Array"></a>Array</h1><p>JavaScript中的数组与其他语言中的数组有很大的区别。JavaScript中的数组可以存不同数据类型的数据，并且数组是动态大小的，会随着数据添加而自动增长。</p>
<h2 id="创建数组"><a href="#创建数组" class="headerlink" title="创建数组"></a>创建数组</h2><p>有几种方式可以创建数组，下面一一介绍。</p>
<h3 id="使用Array构造函数创建数组："><a href="#使用Array构造函数创建数组：" class="headerlink" title="使用Array构造函数创建数组："></a>使用Array构造函数创建数组：</h3><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> colors = <span class="keyword">new</span> <span class="title class_">Array</span>();</span><br></pre></td></tr></tbody></table></figure>
<p>还可以给构造函数传入一个数值，创建指初始容量的数组：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> colors = <span class="keyword">new</span> <span class="title class_">Array</span>(<span class="number">20</span>);</span><br></pre></td></tr></tbody></table></figure>
<p>也可以给构造函数传入要插入到数组中的元素：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> colors = <span class="keyword">new</span> <span class="title class_">Array</span>(<span class="string">"red"</span>,<span class="string">"blue"</span>,<span class="string">"green"</span>);</span><br></pre></td></tr></tbody></table></figure>
<p>也可以省略<code>new</code>操作符：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> colors = <span class="title class_">Array</span>(<span class="number">3</span>);</span><br><span class="line"><span class="keyword">let</span> names = <span class="title class_">Array</span>(<span class="string">"吴大孬"</span>,<span class="string">"二妹"</span>);</span><br></pre></td></tr></tbody></table></figure>
<h3 id="使用数组字面量创建数组"><a href="#使用数组字面量创建数组" class="headerlink" title="使用数组字面量创建数组"></a>使用数组字面量创建数组</h3><p>可以像下面这样用数组字面量创建数组：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> colors = [<span class="string">"red"</span>,<span class="string">"green"</span>,<span class="string">"white"</span>];</span><br></pre></td></tr></tbody></table></figure>
<h3 id="from-与of-方法"><a href="#from-与of-方法" class="headerlink" title="from()与of()方法"></a><code>from()</code>与<code>of()</code>方法</h3><p><code>from()</code>和<code>of()</code>都是用来创建数组的静态方法。<code>from()</code>用于将类数组结构转化为数组实例，<code>of()</code>方法用于将一组参数转换为数组实例。</p>
<p><code>Array.from()</code> 方法用于从一个类似数组或可迭代对象中创建一个新的数组实例。它接收一个可迭代对象（如字符串、类数组对象或 Set）或一个类似数组的对象，并将其转换为一个真正的数组。这个方法可以接收一个可选的映射函数和一个可选的上下文对象，用于对原始元素进行映射或调整。</p>
<p>下面是一个使用 <code>Array.from()</code> 方法的示例：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 字符串转换为数组</span></span><br><span class="line"><span class="keyword">const</span> str = <span class="string">'Hello'</span>;</span><br><span class="line"><span class="keyword">const</span> arr = <span class="title class_">Array</span>.<span class="title function_">from</span>(str);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr); <span class="comment">// ['H', 'e', 'l', 'l', 'o']</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 可迭代对象转换为数组</span></span><br><span class="line"><span class="keyword">const</span> set = <span class="keyword">new</span> <span class="title class_">Set</span>([<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>]);</span><br><span class="line"><span class="keyword">const</span> arr2 = <span class="title class_">Array</span>.<span class="title function_">from</span>(set);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr2); <span class="comment">// ['a', 'b', 'c']</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用映射函数</span></span><br><span class="line"><span class="keyword">const</span> numbers = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>];</span><br><span class="line"><span class="keyword">const</span> doubled = <span class="title class_">Array</span>.<span class="title function_">from</span>(numbers, <span class="function"><span class="params">num</span> =&gt;</span> num * <span class="number">2</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(doubled); <span class="comment">// [2, 4, 6, 8, 10]</span></span><br></pre></td></tr></tbody></table></figure>
<p><code>Array.of()</code> 方法用于创建一个包含任意数量参数的新数组。不同于 <code>Array</code> 构造函数，<code>Array.of()</code> 方法会将传递的参数作为数组的元素，而不是将它们视为单个参数或数组长度。这对于创建具有确定元素的新数组很有用。</p>
<p>下面是一个使用 <code>Array.of()</code> 方法的示例：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr1 = <span class="title class_">Array</span>.<span class="title function_">of</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr1); <span class="comment">// [1, 2, 3, 4, 5]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr2 = <span class="title class_">Array</span>.<span class="title function_">of</span>(<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr2); <span class="comment">// ['a', 'b', 'c']</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> arr3 = <span class="title class_">Array</span>.<span class="title function_">of</span>(<span class="number">1</span>, <span class="string">'two'</span>, [<span class="number">3</span>, <span class="number">4</span>], { <span class="attr">five</span>: <span class="number">5</span> });</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr3); <span class="comment">// [1, 'two', [3, 4], { five: 5 }]</span></span><br></pre></td></tr></tbody></table></figure>
<h2 id="数组空位"><a href="#数组空位" class="headerlink" title="数组空位"></a>数组空位</h2><p>JavaScript 数组空位指的是数组中某些位置上没有任何值的情况。空位在数组中是一种特殊的值，表示该位置上缺少元素。</p>
<p><strong>1. 访问和遍历空位：</strong> 当数组中存在空位时，使用索引访问空位时将返回 <code>undefined</code>。例如：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>, , <span class="number">3</span>];</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr[<span class="number">1</span>]); <span class="comment">// undefined</span></span><br></pre></td></tr></tbody></table></figure>
<p>在使用 <code>for...in</code> 或 <code>for...of</code> 循环遍历数组时，空位会被跳过。例如：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>, , <span class="number">3</span>];</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> item <span class="keyword">of</span> arr) {</span><br><span class="line">  <span class="variable language_">console</span>.<span class="title function_">log</span>(item); <span class="comment">// 1, 3</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p><strong>2. 数组方法对待空位的不同：</strong> 不同的数组方法在处理空位时可能会表现出不同的行为。</p>
<ul>
<li><code>forEach()</code>、<code>map()</code>、<code>filter()</code> 和 <code>reduce()</code> 等迭代方法会跳过空位。</li>
<li><code>join()</code> 和 <code>toString()</code> 方法会将空位视为空字符串。</li>
<li><code>indexOf()</code>、<code>find()</code>、<code>findIndex()</code> 等查找方法无法找到空位。</li>
<li><code>push()</code> 方法会将空位视为有效元素，将其添加到数组末尾。</li>
<li><code>slice()</code> 方法会跳过空位，返回一个新数组。</li>
<li><code>sort()</code> 方法会将空位视为 undefined，并根据 undefined 的规则进行排序。</li>
</ul>
<p>下面是一些示例：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>, , <span class="number">3</span>];</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr.<span class="property">forEach</span>); <span class="comment">// [Function: forEach]</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr.<span class="property">map</span>); <span class="comment">// [Function: map]</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr.<span class="title function_">join</span>()); <span class="comment">// "1,,3"</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr.<span class="title function_">toString</span>()); <span class="comment">// "1,,3"</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr.<span class="title function_">indexOf</span>(<span class="literal">undefined</span>)); <span class="comment">// -1</span></span><br><span class="line"></span><br><span class="line">arr.<span class="title function_">push</span>(<span class="number">4</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr); <span class="comment">// [1, , 3, 4]</span></span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr.<span class="title function_">slice</span>()); <span class="comment">// [1, , 3, 4]</span></span><br><span class="line"></span><br><span class="line">arr.<span class="title function_">sort</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr); <span class="comment">// [1, 3, 4, undefined]</span></span><br></pre></td></tr></tbody></table></figure>
<p><strong>3. 删除空位：</strong> 要从数组中删除空位，可以使用 <code>delete</code> 操作符或 <code>Array.prototype.filter()</code> 方法。例如：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>, , <span class="number">3</span>];</span><br><span class="line"><span class="keyword">delete</span> arr[<span class="number">1</span>];</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(arr); <span class="comment">// [1, empty, 3]</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> filteredArr = arr.<span class="title function_">filter</span>(<span class="function">() =&gt;</span> <span class="literal">true</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(filteredArr); <span class="comment">// [1, 3]</span></span><br></pre></td></tr></tbody></table></figure>
<p>需要注意的是，使用 <code>delete</code> 操作符删除空位后，数组的长度不会改变，仍然保持原来的长度。</p>
<p>尽管 JavaScript 允许数组中存在空位，但在大多数情况下，建议避免使用空位，以避免产生意外的行为。</p>
<h2 id="数组索引"><a href="#数组索引" class="headerlink" title="数组索引"></a>数组索引</h2><p>与其他语言中的数组不同，JavaScript数组若使用超过范围的索引访问数组，数组长度会自动扩展到该索引值加1。</p>
<p>数组中元素的数量保存在数组的<code>length</code>属性中：</p>
<figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> colors = [<span class="string">"red"</span>,<span class="string">"green"</span>,<span class="string">"white"</span>];</span><br><span class="line"><span class="title function_">alert</span>(colors.<span class="property">length</span>); <span class="comment">// 3</span></span><br></pre></td></tr></tbody></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/28/Java%EF%BC%9A%E5%B8%B8%E7%94%A8%E5%AE%9E%E7%94%A8%E7%B1%BB/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/28/Java%EF%BC%9A%E5%B8%B8%E7%94%A8%E5%AE%9E%E7%94%A8%E7%B1%BB/" class="post-title-link" itemprop="url">未命名</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-06-28 14:45:19" itemprop="dateCreated datePublished" datetime="2023-06-28T14:45:19+08:00">2023-06-28</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="String类"><a href="#String类" class="headerlink" title="String类"></a>String类</h1><p>String类常用方法：</p>
<ul>
<li><code>length()</code>：返回字符串的长度。</li>
<li><code>charAt(int index)</code>：返回指定索引位置的字符。</li>
<li><code>substring(int beginIndex)</code>：返回从指定索引开始到字符串末尾的子字符串。</li>
<li><code>substring(int beginIndex, int endIndex)</code>：返回从指定起始索引到结束索引之间的子字符串。</li>
<li><code>equals(Object obj)</code>：比较字符串是否与指定的对象相等。</li>
<li><code>equalsIgnoreCase(String anotherString)</code>：忽略大小写比较字符串是否与指定字符串相等。</li>
<li><code>compareTo(String anotherString)</code>：按字典顺序比较字符串。</li>
<li><code>indexOf(String str)</code>：返回指定子字符串在字符串中首次出现的索引。</li>
<li><code>lastIndexOf(String str)</code>：返回指定子字符串在字符串中最后一次出现的索引。</li>
<li><code>contains(CharSequence sequence)</code>：判断字符串是否包含指定的字符序列。</li>
<li><code>startsWith(String prefix)</code>：判断字符串是否以指定的前缀开始。</li>
<li><code>endsWith(String suffix)</code>：判断字符串是否以指定的后缀结束。</li>
<li><code>isEmpty()</code>：判断字符串是否为空。</li>
<li><code>toLowerCase()</code>：将字符串转换为小写。</li>
<li><code>toUpperCase()</code>：将字符串转换为大写。</li>
<li><code>trim()</code>：去除字符串首尾的空格。</li>
<li><code>replace(char oldChar, char newChar)</code>：替换字符串中的字符。</li>
<li><code>valueOf()</code>：将其他类型的值转换为字符串。</li>
</ul>
<h1 id="正则表达式"><a href="#正则表达式" class="headerlink" title="正则表达式"></a>正则表达式</h1><ul>
<li>字符类：用于匹配单个字符的集合。例如，<code>[abc]</code>表示匹配字符a、b或c。</li>
<li>元字符：具有特殊意义的字符，例如<code>.</code>表示匹配任意字符，<code>\\d</code>表示匹配0~9中任意一个数字。</li>
<li>量词：用于指定匹配次数的修饰符。例如，<code>+</code>表示匹配前面的字符一次或多次，<code>?</code>表示匹配前面的字符零次或一次。</li>
<li>转义字符：用于匹配特殊字符本身而非其特殊含义。例如，<code>\.</code>表示匹配句点字符（.）而不是任意字符。</li>
<li>反义字符类：表示不匹配某个字符类的字符。例如，<code>[^abc]</code>表示匹配除了a、b、c之外的任意字符。</li>
</ul>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/20/Java%EF%BC%9A%E6%8E%A5%E5%8F%A3/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/20/Java%EF%BC%9A%E6%8E%A5%E5%8F%A3/" class="post-title-link" itemprop="url">Java：接口</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-06-20 23:08:45 / 修改时间：23:52:31" itemprop="dateCreated datePublished" datetime="2023-06-20T23:08:45+08:00">2023-06-20</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-后端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="接口的概念"><a href="#接口的概念" class="headerlink" title="接口的概念"></a>接口的概念</h1><h2 id="什么是接口"><a href="#什么是接口" class="headerlink" title="什么是接口"></a>什么是接口</h2><p>接口是Java的一个重要概念，接口定义了一组方法，用于指明实现这个接口的类需要做什么。<strong>接口中的方法都是抽象方法</strong>，只声明了方法的名称、参数列表和返回类型，没有提供方法的具体实现。</p>
<h2 id="声明接口"><a href="#声明接口" class="headerlink" title="声明接口"></a>声明接口</h2><p>可以使用<code>interface</code>关键字声明一个接口。声明接口时要注意，接口中所有的方法只能是抽象方法，方法的修饰符通常省略，默认为<code>public abstract</code>。并且，接口中的字段只能是常量，字段的修饰符默认为<code>public static final</code>，可以省略。</p>
<p>下面是一个声明接口的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">MyInterface</span> {</span><br><span class="line">    <span class="comment">// 声明常量字段</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">MAX_VALUE</span> <span class="operator">=</span> <span class="number">100</span>;</span><br><span class="line">    <span class="comment">// 声明方法</span></span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span>;</span><br><span class="line">    <span class="type">int</span> <span class="title function_">method2</span><span class="params">(String str)</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="如何实现接口"><a href="#如何实现接口" class="headerlink" title="如何实现接口"></a>如何实现接口</h2><p>要想让一个类实现一个接口，需要完成下面两个步骤：</p>
<ol>
<li>用<code>implements</code>关键字将类声明为实现给定的接口，即让类实现某个接口。</li>
<li>在类中<strong>对接口中的所有方法提供定义</strong>。</li>
</ol>
<h1 id="接口的特性与作用"><a href="#接口的特性与作用" class="headerlink" title="接口的特性与作用"></a>接口的特性与作用</h1><h2 id="接口的特性"><a href="#接口的特性" class="headerlink" title="接口的特性"></a>接口的特性</h2><p>接口有以下几个特性：</p>
<ol>
<li>接口中的方法都是隐式地被声明为<code>public abstract</code>，所以在实现接口时，方法不能有具体的实现。</li>
<li>接口不能被实例化，即不能创建接口的对象。但是可以声明接口的变量，并且这个接口变量必须引用实现了这个接口的类对象。</li>
<li>接口可以被其他接口继承，通过关键字<code>extends</code>。一个接口可以继承多个接口。</li>
</ol>
<h2 id="接口的作用"><a href="#接口的作用" class="headerlink" title="接口的作用"></a>接口的作用</h2><ol>
<li>定义规范：接口定义了一组方法，可以作为一种规范或契约，要求实现类按照接口声明的方法来实现。</li>
<li>实现多态：通过接口，可以在不同的类中实现相同的接口方法，从而实现多态的特性。</li>
<li>实现解耦：接口将方法的声明和实现分离，使得代码更加灵活和可维护。</li>
</ol>
<h1 id="接口中的其他方法"><a href="#接口中的其他方法" class="headerlink" title="接口中的其他方法"></a>接口中的其他方法</h1><h2 id="静态和私有方法"><a href="#静态和私有方法" class="headerlink" title="静态和私有方法"></a>静态和私有方法</h2><p>从Java 8开始，接口中可以包含静态方法和私有方法，这为接口的设计和实现带来了更多的灵活性和功能性。</p>
<h3 id="接口中的静态方法"><a href="#接口中的静态方法" class="headerlink" title="接口中的静态方法"></a>接口中的静态方法</h3><p>接口中的静态方法有以下几个特性：</p>
<ul>
<li>接口中的静态方法使用<code>static</code>关键字进行声明。</li>
<li>静态方法可以通过接口名称直接调用，无需实现类的实例。</li>
<li>静态方法不能被实现类所继承或覆盖。</li>
<li>静态方法通常用于提供与接口相关的实用工具方法或公共功能。</li>
</ul>
<p>下面是一个包含静态方法的接口示例：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">MyInterface</span> {</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span>; <span class="comment">// 抽象方法</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">staticMethod</span><span class="params">()</span> {</span><br><span class="line">        <span class="comment">// 静态方法的实现</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h3 id="接口中的私有方法"><a href="#接口中的私有方法" class="headerlink" title="接口中的私有方法"></a>接口中的私有方法</h3><ul>
<li>接口中的私有方法使用<code>private</code>关键字进行声明。</li>
<li>私有方法只能在接口内部被调用，用于封装和复用共享逻辑，提高代码的可读性和可维护性。</li>
<li>私有方法可以被默认方法或其他私有方法所调用。</li>
</ul>
<p>下面是一个包含私有方法的接口示例：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">MyInterface</span> {</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span>; <span class="comment">// 抽象方法</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">default</span> <span class="keyword">void</span> <span class="title function_">defaultMethod</span><span class="params">()</span> {</span><br><span class="line">        <span class="comment">// 默认方法的实现</span></span><br><span class="line">        privateMethod();</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">privateMethod</span><span class="params">()</span> {</span><br><span class="line">        <span class="comment">// 私有方法的实现</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="默认方法"><a href="#默认方法" class="headerlink" title="默认方法"></a>默认方法</h2><p>从Java 8开始，接口可以包含默认方法（使用<code>default</code>关键字），这使得接口可以提供方法的默认实现。默认方法可以有方法体，并且实现类可以选择性地覆盖默认方法。</p>
<h2 id="默认方法冲突"><a href="#默认方法冲突" class="headerlink" title="默认方法冲突"></a>默认方法冲突</h2><p>当一个类实现了多个接口，并且这些接口中包含了具有相同签名的默认方法时，就会出现默认方法冲突。为了解决这种冲突，Java接口的默认方法冲突解决机制提供了以下几种规则：</p>
<ol>
<li>类优先原则（Class Wins Rule）：<ul>
<li>如果一个类直接实现了某个接口，并且同时继承了另一个类（可以是具体类或抽象类），那么在默认方法冲突时，类中定义的方法优先于接口中的默认方法。</li>
<li>这意味着类中的方法将覆盖接口中的默认方法，无论接口中的默认方法是新添加的还是继承的。</li>
</ul>
</li>
<li>接口冲突解决：<ul>
<li>如果一个类实现了多个接口，并且这些接口中包含了具有相同签名的默认方法，那么实现类必须显式地解决默认方法的冲突，通过覆盖默认方法并提供自己的实现。</li>
<li>实现类必须选择要使用的默认方法，或者完全忽略这些默认方法，并提供自己的实现。</li>
</ul>
</li>
<li>显式调用冲突的默认方法：<ul>
<li>如果实现类需要在自己的方法中调用冲突的默认方法，可以使用以下语法：<code>InterfaceName.super.methodName()</code>。</li>
<li>这样可以明确指定要调用的接口中的默认方法。</li>
</ul>
</li>
</ol>
<p>下面是一个解决默认方法冲突的示例：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">InterfaceA</span> {</span><br><span class="line">    <span class="keyword">default</span> <span class="keyword">void</span> <span class="title function_">method</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"InterfaceA"</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">InterfaceB</span> {</span><br><span class="line">    <span class="keyword">default</span> <span class="keyword">void</span> <span class="title function_">method</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"InterfaceB"</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClass</span> <span class="keyword">implements</span> <span class="title class_">InterfaceA</span>, InterfaceB {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method</span><span class="params">()</span> {</span><br><span class="line">        InterfaceA.<span class="built_in">super</span>.method(); <span class="comment">// 显式调用 InterfaceA 的默认方法</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h1 id="接口回调"><a href="#接口回调" class="headerlink" title="接口回调"></a>接口回调</h1><h2 id="什么是接口回调"><a href="#什么是接口回调" class="headerlink" title="什么是接口回调"></a>什么是接口回调</h2><p>在Java中，接口回调是一种常见的设计模式，用于实现对象之间的通信和交互。它允许一个对象（回调接口的实现者）通过接口提供一组方法，然后将该对象的实例传递给另一个对象（调用者），以便在适当的时候调用回调方法来完成特定的操作。</p>
<p>接口回调的基本思想是通过接口来定义一组回调方法，以及一个具体类来实现这个接口并提供回调方法的具体实现。然后，其他对象可以持有实现了回调接口的对象，并在需要时调用其回调方法。</p>
<h2 id="接口回调的步骤"><a href="#接口回调的步骤" class="headerlink" title="接口回调的步骤"></a>接口回调的步骤</h2><ul>
<li>定义回调接口：首先，需要定义一个接口，其中声明了回调方法的签名。这些回调方法是由回调对象来实现和提供具体的实现逻辑。</li>
<li>实现回调接口：然后，创建一个类来实现回调接口，并实现接口中声明的回调方法。该类将提供具体的实现逻辑，即在回调方法中执行期望的操作。</li>
<li>注册回调对象：其他对象需要与回调对象进行通信。它们可以将回调对象的实例传递给调用者，并将其注册为回调对象。</li>
<li>调用回调方法：在合适的时机，调用者可以通过回调对象的引用调用回调方法来触发特定的操作。</li>
</ul>
<h2 id="接口回调的作用"><a href="#接口回调的作用" class="headerlink" title="接口回调的作用"></a>接口回调的作用</h2><ul>
<li>实现事件处理：当某个事件发生时，可以通过接口回调机制将事件传递给相应的处理对象，并执行相应的操作。例如，按钮点击事件、网络请求完成等。</li>
<li>实现插件机制：通过接口回调，可以实现插件式开发，允许在运行时动态地扩展和替换对象的功能。插件可以通过实现特定的回调接口来添加新的行为或修改现有行为。</li>
<li>解耦对象间的依赖：接口回调可以帮助对象之间解耦，减少它们之间的直接依赖关系。调用者只需要知道回调接口，并不需要知道具体的实现类，从而提高代码的可维护性和扩展性。</li>
<li>实现回调机制：接口回调是实现回调模式的一种常见方式。回调模式允许对象在不同的上下文中进行通信和交互，从而实现灵活的程序设计。</li>
</ul>
<p>通过接口回调，Java程序可以实现对象之间的松耦合通信，并提供灵活的扩展和定制化能力。它被广泛应用于事件处理、回调机制、插件化架构等场景。</p>
<h2 id="接口回调举例"><a href="#接口回调举例" class="headerlink" title="接口回调举例"></a>接口回调举例</h2><p>下面是一个简单的例子，演示了使用接口回调来处理按钮点击事件：</p>
<p>首先，定义一个回调接口 <code>ButtonClickListener</code>，其中声明了一个回调方法 <code>onClick()</code>：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">ButtonClickListener</span> {</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">onClick</span><span class="params">()</span>;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>然后，创建一个按钮类 <code>Button</code>，其中包含一个 <code>ButtonClickListener</code> 类型的成员变量，并提供一个注册回调的方法 <code>setClickListener()</code> 和触发回调的方法 <code>click()</code>：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Button</span> {</span><br><span class="line">    <span class="keyword">private</span> ButtonClickListener clickListener;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setClickListener</span><span class="params">(ButtonClickListener listener)</span> {</span><br><span class="line">        <span class="built_in">this</span>.clickListener = listener;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">click</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">if</span> (clickListener != <span class="literal">null</span>) {</span><br><span class="line">            clickListener.onClick();</span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>接下来，创建一个实现了 <code>ButtonClickListener</code> 接口的类 <code>MyButtonClickListener</code>，并实现回调方法 <code>onClick()</code>：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyButtonClickListener</span> <span class="keyword">implements</span> <span class="title class_">ButtonClickListener</span> {</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">onClick</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Button clicked!"</span>);</span><br><span class="line">        <span class="comment">// 执行其他操作...</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>最后，在主程序中使用接口回调：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">Button</span> <span class="variable">button</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Button</span>();</span><br><span class="line">        <span class="type">ButtonClickListener</span> <span class="variable">listener</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyButtonClickListener</span>();</span><br><span class="line">        button.setClickListener(listener);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 模拟用户点击按钮</span></span><br><span class="line">        button.click();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/15/Java%EF%BC%9A%E7%BB%A7%E6%89%BF/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/15/Java%EF%BC%9A%E7%BB%A7%E6%89%BF/" class="post-title-link" itemprop="url">Java：继承</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-06-15 15:53:20 / 修改时间：17:19:52" itemprop="dateCreated datePublished" datetime="2023-06-15T15:53:20+08:00">2023-06-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-后端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="类、超类和子类"><a href="#类、超类和子类" class="headerlink" title="类、超类和子类"></a>类、超类和子类</h1><h2 id="定义子类"><a href="#定义子类" class="headerlink" title="定义子类"></a>定义子类</h2><p>可以用<code>extends</code>继承一个类：<br></p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Manager</span> <span class="keyword">extends</span> <span class="title class_">Employee</span> {</span><br><span class="line">    <span class="comment">// 为子类添加属性方法</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p></p>
<p>上面例子中的<code>Manager</code>类称为<strong>超类</strong>，<code>Employee</code>类称为<strong>子类</strong>。子类拥有的功能比超类多。</p>
<p>扩展超类定义子类时，只需要指出子类与超类的不同之处。因此，设计类的时候，应该将最一般的字段和方法放在超类中，将更特殊的字段和方法放在子类中。</p>
<h2 id="覆盖（重写）方法"><a href="#覆盖（重写）方法" class="headerlink" title="覆盖（重写）方法"></a>覆盖（重写）方法</h2><p><strong>方法重写</strong>是指子类对父类中已经存在的方法进行重新实现的过程。当子类继承父类时，可以通过重写父类的方法来修改或扩展其行为，使得子类在调用该方法时执行自己的实现逻辑。</p>
<p>重写方法的规则如下：</p>
<ol>
<li>方法签名：子类中重写的方法必须与父类中被重写的方法具有相同的方法签名。方法签名包括方法名、参数列表和参数顺序。</li>
<li>访问修饰符：子类中重写的方法的访问修饰符不能比父类中被重写的方法更严格。例如，如果父类方法是<code>public</code>，则子类方法可以是<code>public</code>或<code>protected</code>，但不能是<code>private</code>。</li>
<li>返回类型：子类中重写的方法的返回类型必须与父类中被重写方法的返回类型兼容。子类的返回类型可以是父类方法返回类型的子类型（协变类型）。</li>
<li>异常：子类中重写的方法可以声明与父类方法相同的异常，或者不声明异常。子类重写方法声明的异常不能比父类方法声明的异常更广泛。</li>
<li>方法体：子类中重写的方法需要提供自己的实现逻辑。子类方法可以调用父类的方法（使用<code>super</code>关键字）以保留父类的行为，或者完全覆盖父类方法。</li>
</ol>
<p>来看一个方法重写的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">makeSound</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Animal makes sound."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cat</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">makeSound</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"喵喵喵"</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Dog</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">makeSound</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"汪汪汪"</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">Animal</span> <span class="variable">animal1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Animal</span>();</span><br><span class="line">        <span class="type">Animal</span> <span class="variable">animal2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cat</span>();</span><br><span class="line">        <span class="type">Animal</span> <span class="variable">animal3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dog</span>();</span><br><span class="line">        </span><br><span class="line">        animal1.makeSound(); <span class="comment">// 输出：Animal makes sound.</span></span><br><span class="line">        animal2.makeSound(); <span class="comment">// 输出：喵喵喵.</span></span><br><span class="line">        animal3.makeSound(); <span class="comment">// 输出：汪汪汪.</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="super关键字"><a href="#super关键字" class="headerlink" title="super关键字"></a>super关键字</h2><p><code>super</code>关键字用于引用当前对象所直接继承的父类的成员。它<strong>可以用于访问父类的属性、方法和构造器</strong>。</p>
<p><code>super</code>关键字的用法有以下几种：</p>
<ol>
<li>访问父类的属性：使用<code>super</code>关键字可以在子类中访问父类中被隐藏的属性。当子类和父类拥有同名的属性时，可以使用<code>super</code>关键字来区分访问哪个类的属性。</li>
<li>调用父类的方法：使用<code>super</code>关键字可以在子类中调用父类中被重写的方法。当子类重写了父类的方法，但仍然需要调用父类的方法时，可以使用<code>super</code>关键字。</li>
<li>调用父类的构造器：使用<code>super</code>关键字可以在子类的构造器中调用父类的构造器。通过调用父类的构造器，可以完成对父类成员的初始化。</li>
</ol>
<p>来看<code>super</code>关键字使用的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="keyword">protected</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Animal</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Animal eats."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Cat</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Cat</span><span class="params">(String name, <span class="type">int</span> age)</span> {</span><br><span class="line">        <span class="built_in">super</span>(name); <span class="comment">// 调用父类的构造器</span></span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span> {</span><br><span class="line">        <span class="built_in">super</span>.eat(); <span class="comment">// 调用父类的eat方法</span></span><br><span class="line">        System.out.println(<span class="string">"Cat eats fish."</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">printInfo</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Name: "</span> + <span class="built_in">super</span>.name); <span class="comment">// 访问父类的属性</span></span><br><span class="line">        System.out.println(<span class="string">"Age: "</span> + age);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">Cat</span> <span class="variable">cat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cat</span>(<span class="string">"Tom"</span>, <span class="number">3</span>);</span><br><span class="line">        cat.eat(); <span class="comment">// 输出：Animal eats. Cat eats fish.</span></span><br><span class="line">        cat.printInfo(); <span class="comment">// 输出：Name: Tom  Age: 3</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="子类构造器"><a href="#子类构造器" class="headerlink" title="子类构造器"></a>子类构造器</h2><p>子类的构造器用于创建子类对象时对子类进行初始化。子类构造器会自动调用父类的构造器，以完成对父类成员的初始化。子类构造器可以选择调用父类的构造器，也可以不调用，但在不调用父类构造器的情况下，子类对象无法正确初始化父类的成员。</p>
<p>子类的构造器可以有以下几种情况：</p>
<ol>
<li>默认构造器：如果子类没有显式声明构造器，那么会默认生成一个无参的构造器。默认构造器会自动调用父类的无参构造器（如果父类有无参构造器）或者编译错误（如果父类没有无参构造器）。</li>
<li>显式调用父类构造器：子类的构造器可以使用<code>super</code>关键字显式调用父类的构造器。通过<code>super()</code>调用父类的无参构造器，或者通过<code>super(parameters)</code>调用父类的带参构造器。这种情况下，子类的构造器中的第一条语句必须是<code>super()</code>，用于调用父类的构造器。</li>
<li>隐式调用父类构造器：如果子类的构造器没有显式调用父类的构造器，那么编译器会默认在子类的构造器中添加<code>super()</code>语句来隐式调用父类的无参构造器。</li>
</ol>
<p>下面是一个示例，展示了子类构造器的使用：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="keyword">protected</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Animal</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">        System.out.println(<span class="string">"Animal父类构造器"</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cat</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Cat</span><span class="params">(String name, <span class="type">int</span> age)</span> {</span><br><span class="line">        <span class="built_in">super</span>(name); <span class="comment">// 显式调用父类的构造器</span></span><br><span class="line">        <span class="built_in">this</span>.age = age;</span><br><span class="line">        System.out.println(<span class="string">"Cat子类构造器"</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">printInfo</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Name: "</span> + name);</span><br><span class="line">        System.out.println(<span class="string">"Age: "</span> + age);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">Cat</span> <span class="variable">cat</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cat</span>(<span class="string">"Tom"</span>, <span class="number">3</span>);</span><br><span class="line">        cat.printInfo();</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></tbody></table></figure>
<h2 id="多态"><a href="#多态" class="headerlink" title="多态"></a>多态</h2><p>多态性是面向对象编程的一个重要概念，它允许使用一个统一的接口来表示多个不同的对象。多态性使得程序可以根据实际对象的类型，在运行时动态地确定调用哪个对象的方法。</p>
<p>实现多态性的基础是继承和方法重写。通过继承，子类可以继承父类的方法和属性，并且可以根据需要重写父类的方法。当一个父类的引用变量指向子类的对象时，就发生了多态。</p>
<p>多态性的主要特点包括：</p>
<ol>
<li>方法重写：子类可以重写父类的方法，以实现自己的特定行为。当父类的引用变量指向子类对象时，会根据对象的实际类型调用相应的重写方法。</li>
<li>编译时类型和运行时类型：在编译时，引用变量的类型决定了可以调用的方法。在运行时，实际对象的类型决定了调用哪个类的方法。</li>
<li>向上转型：子类对象可以赋值给父类引用变量，这称为向上转型。通过向上转型，可以实现对父类引用变量的多态性使用。</li>
<li>动态绑定：在运行时，会根据对象的实际类型动态地绑定方法的调用。即使引用变量的类型是父类，但实际调用的是子类的重写方法。</li>
</ol>
<p>下面是一个示例，展示了多态的使用：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">makeSound</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Animal makes sound."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Cat</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">makeSound</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Cat meows."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Dog</span> <span class="keyword">extends</span> <span class="title class_">Animal</span> {</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">makeSound</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"Dog barks."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">Animal</span> <span class="variable">animal1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Animal</span>();</span><br><span class="line">        <span class="type">Animal</span> <span class="variable">animal2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Cat</span>();</span><br><span class="line">        <span class="type">Animal</span> <span class="variable">animal3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Dog</span>();</span><br><span class="line">        </span><br><span class="line">        animal1.makeSound(); <span class="comment">// 输出：Animal makes sound.</span></span><br><span class="line">        animal2.makeSound(); <span class="comment">// 输出：Cat meows.</span></span><br><span class="line">        animal3.makeSound(); <span class="comment">// 输出：Dog barks.</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>在上述示例中，<code>Animal</code>类有一个<code>makeSound</code>方法。<code>Cat</code>类和<code>Dog</code>类分别继承自<code>Animal</code>类，并重写了<code>makeSound</code>方法。在<code>Main</code>类的<code>main</code>方法中，创建了一个<code>Animal</code>对象、一个<code>Cat</code>对象和一个<code>Dog</code>对象，并通过相应的引用变量调用了<code>makeSound</code>方法。由于这些对象都是通过父类引用变量指向的，所以在运行时根据对象的实际类型动态地调用了相应的重写方法。</p>
<p>多态性可以使代码更加灵活和可扩展，减少了代码的重复性，提高了代码的可维护性和可读性。它是面向对象编程中的重要特性之一。</p>
<h2 id="阻止继承：final类和方法"><a href="#阻止继承：final类和方法" class="headerlink" title="阻止继承：final类和方法"></a>阻止继承：final类和方法</h2><p>如果在定义类的时候使用了<code>final</code>关键字之后这个类就不允许继承：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">ClassName</span> {</span><br><span class="line">    ···</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>类中的某个特定方法也可以被声明为<code>final</code>。如果这样做，子类就不能覆盖（重写）这个方法。例如：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Employee</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> name;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h1 id="抽象类"><a href="#抽象类" class="headerlink" title="抽象类"></a>抽象类</h1><p>抽象类是一种特殊的类，它不能被实例化，只能被继承。抽象类用于定义一个通用的、抽象的概念，其中可能包含抽象方法和非抽象方法。抽象方法是一种没有实现的方法，它只有方法签名而没有方法体，需要在子类中进行实现。</p>
<p>抽象类的主要特点包括：</p>
<ol>
<li>无法实例化：抽象类不能直接被实例化，只能被用作父类，用于派生子类。</li>
<li>可以包含抽象方法：抽象类可以包含抽象方法，这些方法只有方法签名，没有方法体。抽象方法用于定义接口规范，要求子类对其进行实现。</li>
<li>可以包含非抽象方法：抽象类也可以包含非抽象方法，这些方法有具体的实现代码。子类可以直接继承和使用这些非抽象方法。</li>
<li>子类的责任：如果一个类继承了一个抽象类，它必须实现所有的抽象方法，除非它自己也声明为抽象类。</li>
</ol>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/15/Java%EF%BC%9A%E5%AF%B9%E8%B1%A1%E4%B8%8E%E7%B1%BB/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/15/Java%EF%BC%9A%E5%AF%B9%E8%B1%A1%E4%B8%8E%E7%B1%BB/" class="post-title-link" itemprop="url">Java：对象与类</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2023-06-15 13:55:34 / 修改时间：15:39:52" itemprop="dateCreated datePublished" datetime="2023-06-15T13:55:34+08:00">2023-06-15</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E5%90%8E%E7%AB%AF/" itemprop="url" rel="index"><span itemprop="name">-后端</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="面向对象概述"><a href="#面向对象概述" class="headerlink" title="面向对象概述"></a>面向对象概述</h1><h2 id="类"><a href="#类" class="headerlink" title="类"></a>类</h2><p>当谈到Java的类与对象，我们需要理解面向对象编程（OOP）的概念。Java是一种面向对象的编程语言，类是Java中最基本的概念之一，它体现了面向对象编程的核心思想。</p>
<p>类是一种用来描述对象特征和行为的模板或蓝图。可以将类看作是一个抽象的数据类型，它定义了一组<strong>属性</strong>（也称为<strong>成员变量</strong>和<strong>字段</strong>）和<strong>方法</strong>，用于描述对象的状态和行为。类的实例被称为对象，通过创建对象，我们可以使用类中定义的属性和方法。</p>
<p><strong>Java中的类，包括用户自己定义的类，都继承子<code>Object</code>类</strong>。</p>
<h2 id="对象"><a href="#对象" class="headerlink" title="对象"></a>对象</h2><p>在Java中，对象是类的实例化结果，它是类的具体化和实体化。对象是内存中的一个实体，具有状态（属性）和行为（方法）。也就是说，<strong>属性用来描述对象的状态，方法用来描述对象的行为</strong>。</p>
<h2 id="类之间的关系"><a href="#类之间的关系" class="headerlink" title="类之间的关系"></a>类之间的关系</h2><p>在Java中，类之间最常见的关系有：</p>
<ul>
<li>依赖（use-a）：依赖关系是一种临时性的使用关系，表示一个类在某个特定的方法中使用了另一个类的对象。依赖关系通常表现为方法参数、局部变量或静态方法的调用。</li>
<li>聚合（has-a）：聚合关系是一种特殊的关联关系，表示整体与部分之间的关系。聚合关系中，整体对象包含部分对象，但部分对象可以存在独立于整体对象的生命周期。聚合关系是一种弱关系，整体对象可以存在没有部分对象的情况。</li>
<li>继承（is-a）：通过继承，一个类（子类）可以继承另一个类（父类）的属性和方法。子类可以扩展或修改继承的属性和方法，同时还可以定义自己的新属性和方法。继承关系用关键字<code>extends</code>表示。</li>
</ul>
<p>在Java程序开发中，应该尽可能减少类之间的<strong>耦合</strong>。即应该尽可能地减少类之间的相互依赖，这样，一个类的改变不会导致另一个类产生BUG。</p>
<h1 id="自定义类"><a href="#自定义类" class="headerlink" title="自定义类"></a>自定义类</h1><h2 id="如何创建一个类？"><a href="#如何创建一个类？" class="headerlink" title="如何创建一个类？"></a>如何创建一个类？</h2><p>类的定义形式如下：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ClassName</span> {</span><br><span class="line">    field1 <span class="comment">// 属性</span></span><br><span class="line">    field2</span><br><span class="line">    ···</span><br><span class="line">    constructor1 <span class="comment">// 构造器</span></span><br><span class="line">    constructor2</span><br><span class="line">    ···</span><br><span class="line">    method1 <span class="comment">// 方法</span></span><br><span class="line">    method2</span><br><span class="line">    ···</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="this关键字"><a href="#this关键字" class="headerlink" title="this关键字"></a><code>this</code>关键字</h2><p>在Java中，<code>this</code>是一个特殊的关键字，用于引用当前对象的实例。可以用它引用对象的属性，或返回当前对象。</p>
<h2 id="构造器"><a href="#构造器" class="headerlink" title="构造器"></a>构造器</h2><h3 id="认识构造器"><a href="#认识构造器" class="headerlink" title="认识构造器"></a>认识构造器</h3><p>在Java中，<strong>构造器（Constructor）</strong>是类中一种特殊的方法，用于创建对象并初始化对象的<strong>状态（属性）</strong>。构造器具有与类相同的名称，并且没有返回类型（包括void）。每当使用<code>new</code>关键字创建一个对象时，构造器将被调用。</p>
<h3 id="默认构造器"><a href="#默认构造器" class="headerlink" title="默认构造器"></a>默认构造器</h3><p>如果类没有显式定义构造器，Java会提供一个默认构造器。默认构造器没有参数，并且执行默认的初始化操作。但是，<strong>如果类定义了任何构造器（无论是无参数还是有参数的构造器），则默认构造器不会被提供</strong>。</p>
<h3 id="构造器重载"><a href="#构造器重载" class="headerlink" title="构造器重载"></a>构造器重载</h3><p>一个类可以定义多个构造器，只要它们具有不同的参数列表。这被称为构造器重载。通过提供不同的构造器，可以在创建对象时传递不同的参数组合，以满足不同的初始化需求。</p>
<h3 id="构造器链"><a href="#构造器链" class="headerlink" title="构造器链"></a>构造器链</h3><p>通过使用<code>this</code>关键字，在一个构造器中调用另一个重载的构造器，可以创建构造器链。构造器链允许在构造对象时复用通用的初始化逻辑，从而避免代码重复。</p>
<h3 id="构造器使用例子"><a href="#构造器使用例子" class="headerlink" title="构造器使用例子"></a>构造器使用例子</h3><p>下面是一个示例，展示了如何定义和使用构造器：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> myNumber;</span><br><span class="line">    <span class="keyword">private</span> String myString;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构造器1</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyClass</span><span class="params">()</span> {</span><br><span class="line">        myNumber = <span class="number">0</span>;</span><br><span class="line">        myString = <span class="string">"Default"</span>;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构造器2</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyClass</span><span class="params">(<span class="type">int</span> number, String string)</span> {</span><br><span class="line">        myNumber = number;</span><br><span class="line">        myString = string;</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 构造器3，通过构造器链调用构造器2</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MyClass</span><span class="params">(<span class="type">int</span> number)</span> {</span><br><span class="line">        <span class="built_in">this</span>(number, <span class="string">"Default"</span>);</span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doSomething</span><span class="params">()</span> {</span><br><span class="line">        <span class="comment">// 方法的实现逻辑</span></span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">MyClass</span> <span class="variable">object1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyClass</span>(); <span class="comment">// 使用构造器1</span></span><br><span class="line">        <span class="type">MyClass</span> <span class="variable">object2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyClass</span>(<span class="number">10</span>, <span class="string">"Hello"</span>); <span class="comment">// 使用构造器2</span></span><br><span class="line">        <span class="type">MyClass</span> <span class="variable">object3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyClass</span>(<span class="number">20</span>); <span class="comment">// 使用构造器3</span></span><br><span class="line"></span><br><span class="line">        System.out.println(object1.myNumber);</span><br><span class="line">        System.out.println(object2.myString);</span><br><span class="line">        object3.doSomething();</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="使用var声明局部变量"><a href="#使用var声明局部变量" class="headerlink" title="使用var声明局部变量"></a>使用var声明局部变量</h2><p>在Java 10及其以后的版本中，引入了局部变量类型推断的特性，可以使用<code>var</code>关键字来声明局部变量。使用<code>var</code>可以使编码更加简洁，特别是在需要声明复杂类型的变量时。</p>
<p>使用<code>var</code>关键字声明局部变量时，编译器会根据变量的初始化表达式推断其类型。编译器会根据右侧的表达式进行类型推断，并将推断得到的类型应用于变量。</p>
<p>要注意的是，<code>var</code>关键字只能用于声明局部变量，不能用于成员变量、方法参数、方法返回类型以及静态初始化块中的变量。</p>
<p>下面是使用<code>var</code>声明局部变量的示例：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">var</span> <span class="variable">number</span> <span class="operator">=</span> <span class="number">10</span>; <span class="comment">// 使用var声明整型变量</span></span><br><span class="line">        <span class="type">var</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">"John"</span>; <span class="comment">// 使用var声明字符串变量</span></span><br><span class="line">        <span class="type">var</span> <span class="variable">myList</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;String&gt;(); <span class="comment">// 使用var声明集合变量</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> element : myList) {</span><br><span class="line">            <span class="comment">// 遍历集合中的元素</span></span><br><span class="line">        }</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="处理实例字段为空的情况"><a href="#处理实例字段为空的情况" class="headerlink" title="处理实例字段为空的情况"></a>处理实例字段为空的情况</h2><p>有时在实例化一个类时，可能为某个属性赋值为<code>null</code>，但我们又不希望该属性为空，也不希望为其赋值为<code>null</code>，Java针对此情况提供了两种方法。</p>
<p>一个是宽容型方法，当用户调用构造器为一个属性设置为null时，我们希望将<code>null</code>替换为<code>unknown</code>，可以像下面这样实现：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">Employee</span><span class="params">(String name,<span class="type">double</span> salary)</span> {</span><br><span class="line">    <span class="built_in">this</span>.name = Objects.requireNonNullElse(name,<span class="string">"unknown"</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>一个是严格型方法，直接拒绝null参数：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">Employee</span><span class="params">(String name,<span class="type">double</span> salary)</span> {</span><br><span class="line">    Objects.requireNonNull(name,<span class="string">"name字段不能为空"</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>这样，当一个人用<code>null</code>为<code>name</code>赋值，就会产生一个<code>NullPointerException</code>异常。</p>
<h2 id="隐式参数与显式参数"><a href="#隐式参数与显式参数" class="headerlink" title="隐式参数与显式参数"></a>隐式参数与显式参数</h2><p>显式参数是通过方法调用中的参数列表明确传递给方法的参数。在方法定义和调用过程中，显式参数的名称和类型是明确的，可以直接访问和使用。</p>
<p>隐式参数是指在方法调用中隐含传递给方法的参数，通常是调用方法的对象本身（或者是当前类的实例）。在方法内部，可以通过使用关键字<code>this</code>来引用隐式参数。</p>
<p>显示参数很普通，来看一个隐式参数的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> number;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setNumber</span><span class="params">(<span class="type">int</span> number)</span> {</span><br><span class="line">        <span class="built_in">this</span>.number = number; <span class="comment">// 隐式参数: this</span></span><br><span class="line">    }</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">MyClass</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyClass</span>();</span><br><span class="line">        obj.setNumber(<span class="number">10</span>); <span class="comment">// 隐式参数: obj</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="封装与封装的优点"><a href="#封装与封装的优点" class="headerlink" title="封装与封装的优点"></a>封装与封装的优点</h2><p>在Java中，<strong>封装</strong>是一种面向对象编程的原则和机制，它将数据和操作数据的方法组合在一起，形成一个类，通过限制对类的访问方式来保护数据的安全性。</p>
<p>试想一下，如果将类的属性设置为<code>public</code>，那么任何一个类都可以修改这个类对象的属性，造成数据的破坏。如果将类的属性设置为<code>private</code>，那么这个类的属性就只允许这个类中的方法访问，也就是说，只有这个类实例化的对象才能修改该类属性，简单说就是只允许对象自己修改或访问自己的数据。这就是<strong>封装的思想</strong>。</p>
<p>来看一个例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> {</span><br><span class="line">    <span class="keyword">private</span> String name;<span class="comment">// 将name属性设置为私有的</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Student</span><span class="params">(String name,<span class="type">int</span> age)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">this</span>.name;</span><br><span class="line">    }</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String name)</span> {</span><br><span class="line">        <span class="built_in">this</span>.name = name;</span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<p>上面的例子中，<code>Student</code>类的<code>name</code>属性只允许<code>Student</code>对象的<code>getName()</code>和<code>setName()</code>方法来访问和赋值。</p>
<p>一个很重要的点要注意，一个类的<code>getXXX()</code>与<code>setXXX()</code>方法可以访问和设置这个类的所有对象的私有属性。</p>
<h2 id="final关键字"><a href="#final关键字" class="headerlink" title="## final关键字"></a>## <code>final</code>关键字</h2><p><code>final</code>关键字用于表示不可改变的或不可继承的特性。它可以应用于变量、方法和类。</p>
<h3 id="使用final声明常量"><a href="#使用final声明常量" class="headerlink" title="使用final声明常量"></a>使用<code>final</code>声明常量</h3><p>使用<code>final</code>关键字声明的变量是常量，一旦赋予初始值后就无法再修改。它必须在声明时或构造器中进行初始化。</p>
<h3 id="使用final声明方法"><a href="#使用final声明方法" class="headerlink" title="使用final声明方法"></a>使用<code>final</code>声明方法</h3><p>使用<code>final</code>关键字修饰的方法是不可被子类重写的。当在父类中的方法不希望被子类修改时，可以使用<code>final</code>修饰。</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Parent</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">printMessage</span><span class="params">()</span> {</span><br><span class="line">        System.out.println(<span class="string">"This is a final method."</span>);</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Child</span> <span class="keyword">extends</span> <span class="title class_">Parent</span> {</span><br><span class="line">    <span class="comment">// 无法重写父类的final方法</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h3 id="使用final声明类"><a href="#使用final声明类" class="headerlink" title="使用final声明类"></a>使用<code>final</code>声明类</h3><p>使用<code>final</code>关键字修饰的类是不可被继承的。一旦将类声明为<code>final</code>，其他类无法继承该类。</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">FinalClass</span> {</span><br><span class="line">    <span class="comment">// 类的内容</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 无法从final类继承</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ChildClass</span> <span class="keyword">extends</span> <span class="title class_">FinalClass</span> {</span><br><span class="line">    <span class="comment">// 无法继承final类</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>
<h2 id="静态字段与静态方法"><a href="#静态字段与静态方法" class="headerlink" title="静态字段与静态方法"></a>静态字段与静态方法</h2><p>静态字段和静态方法是与类关联而不是与类的实例关联的成员。它们属于类本身，而不是类的特定实例。也就是说不需要实例化就可以调用类的静态字段与静态方法，以下是关于静态字段和静态方法的介绍。</p>
<h3 id="静态字段"><a href="#静态字段" class="headerlink" title="静态字段"></a>静态字段</h3><p>下面是静态字段的一些说明：</p>
<ul>
<li>静态字段是使用<code>static</code>关键字声明的类级别变量，它被所有类的实例共享。</li>
<li>静态字段在内存中只有一份副本，无论创建多少个类的实例，都共享相同的静态字段。</li>
<li>静态字段可以通过类名直接访问，无需实例化类对象。</li>
<li>静态字段常用于表示与类相关的常量、配置信息或共享状态。</li>
</ul>
<p>来看一个例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> count; <span class="comment">// 静态字段</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">increment</span><span class="params">()</span> {</span><br><span class="line">        count++; <span class="comment">// 可以在实例方法中访问静态字段</span></span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 访问静态字段</span></span><br><span class="line">MyClass.count = <span class="number">10</span>;</span><br><span class="line"><span class="type">int</span> <span class="variable">currentCount</span> <span class="operator">=</span> MyClass.count;</span><br></pre></td></tr></tbody></table></figure>
<h3 id="静态方法"><a href="#静态方法" class="headerlink" title="静态方法"></a>静态方法</h3><p>下面是静态方法的一些说明：</p>
<ul>
<li>静态方法是使用<code>static</code>关键字修饰的类级别方法，它不属于类的实例，而是属于类本身。</li>
<li>静态方法可以通过类名直接调用，无需实例化类对象。</li>
<li>静态方法只能直接访问静态字段和调用其他静态方法，无法直接访问实例字段和调用实例方法。</li>
<li>静态方法常用于执行与类相关的操作，例如工具方法、实用方法或创建实例的工厂方法。</li>
</ul>
<p>来看一个例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MathUtils</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">sum</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> {</span><br><span class="line">        <span class="keyword">return</span> a + b;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 调用静态方法</span></span><br><span class="line"><span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> MathUtils.sum(<span class="number">3</span>, <span class="number">5</span>);</span><br></pre></td></tr></tbody></table></figure>
<h2 id="工厂方法"><a href="#工厂方法" class="headerlink" title="工厂方法"></a>工厂方法</h2><p><strong>==暂时不会，等补充==</strong></p>
<h2 id="重载"><a href="#重载" class="headerlink" title="重载"></a>重载</h2><p><strong>重载</strong>是指在同一个类中可以定义多个具有相同名称但参数列表不同的方法。重载允许使用相同的方法名来执行不同的操作，根据不同的参数类型、参数个数或参数顺序来区分方法。</p>
<p>以下是方法重载的特点和规则：</p>
<ol>
<li>方法签名：方法的签名由方法名和参数列表组成。参数列表包括参数的类型、顺序和个数。方法的返回类型不是方法签名的一部分，因此不能仅仅通过返回类型的不同来重载方法。</li>
<li>参数列表的不同：方法重载要求参数列表不同，可以通过参数的类型、个数和顺序来区分方法。即使返回类型相同，只有参数列表不同的方法也可以进行重载。</li>
<li>访问修饰符和异常：重载的方法可以具有不同的访问修饰符（public、private、protected等）和不同的异常声明，但这些因素不会影响方法的重载。</li>
<li>返回类型：方法的返回类型不是重载方法的条件。如果仅返回类型不同，而其他方面相同（如参数列表），则会导致编译错误。</li>
<li>调用重载方法：在调用重载方法时，编译器会根据参数列表的匹配程度选择合适的方法。如果有多个方法都符合参数列表，编译器会根据最佳匹配原则选择最具体的方法。</li>
</ol>
<p>下面是一个方法重载的例子：</p>
<figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Calculator</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b)</span> {</span><br><span class="line">        <span class="keyword">return</span> a + b;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">add</span><span class="params">(<span class="type">double</span> a, <span class="type">double</span> b)</span> {</span><br><span class="line">        <span class="keyword">return</span> a + b;</span><br><span class="line">    }</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">add</span><span class="params">(<span class="type">int</span> a, <span class="type">int</span> b, <span class="type">int</span> c)</span> {</span><br><span class="line">        <span class="keyword">return</span> a + b + c;</span><br><span class="line">    }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line">        <span class="type">Calculator</span> <span class="variable">calculator</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Calculator</span>();</span><br><span class="line">        <span class="type">int</span> <span class="variable">result1</span> <span class="operator">=</span> calculator.add(<span class="number">2</span>, <span class="number">3</span>); <span class="comment">// 调用add(int a, int b)</span></span><br><span class="line">        <span class="type">double</span> <span class="variable">result2</span> <span class="operator">=</span> calculator.add(<span class="number">2.5</span>, <span class="number">3.7</span>); <span class="comment">// 调用add(double a, double b)</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">result3</span> <span class="operator">=</span> calculator.add(<span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>); <span class="comment">// 调用add(int a, int b, int c)</span></span><br><span class="line">    }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://xiaoqingming18.github.io/2023/06/13/CPU/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="鸣蜩十七">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="鸣蜩十七">
      <meta itemprop="description" content="记录我的学习、生活和恋爱日常">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | 鸣蜩十七">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2023/06/13/CPU/" class="post-title-link" itemprop="url">未命名</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2023-06-13 21:28:21" itemprop="dateCreated datePublished" datetime="2023-06-13T21:28:21+08:00">2023-06-13</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="CPU的功能"><a href="#CPU的功能" class="headerlink" title="CPU的功能"></a>CPU的功能</h1><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612081126525.png" alt="image-20230612081126525"></p>
<h2 id="运算器和控制器的功能"><a href="#运算器和控制器的功能" class="headerlink" title="运算器和控制器的功能"></a>运算器和控制器的功能</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612081321513.png" alt="image-20230612081321513"></p>
<h2 id="运算器的基本结构"><a href="#运算器的基本结构" class="headerlink" title="运算器的基本结构"></a>运算器的基本结构</h2><h3 id="专用数据通路方式"><a href="#专用数据通路方式" class="headerlink" title="专用数据通路方式"></a>专用数据通路方式</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612081906918.png" alt="image-20230612081906918"></p>
<h3 id="CPU内部单总线方式"><a href="#CPU内部单总线方式" class="headerlink" title="CPU内部单总线方式"></a>CPU内部单总线方式</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612082555539.png" alt="image-20230612082555539"></p>
<h2 id="控制器的基本结构"><a href="#控制器的基本结构" class="headerlink" title="控制器的基本结构"></a>控制器的基本结构</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612083037159.png" alt="image-20230612083037159"></p>
<h2 id="CPU结构"><a href="#CPU结构" class="headerlink" title="CPU结构"></a>CPU结构</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612083239622.png" alt="image-20230612083239622"></p>
<p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612083248525.png" alt="image-20230612083248525"></p>
<h2 id="回顾"><a href="#回顾" class="headerlink" title="回顾"></a>回顾</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612083348383.png" alt="image-20230612083348383"></p>
<h1 id="指令周期"><a href="#指令周期" class="headerlink" title="指令周期"></a>指令周期</h1><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612084038934.png" alt="image-20230612084038934"></p>
<p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612084415888.png" alt="image-20230612084415888"></p>
<h2 id="各种周期关系举例"><a href="#各种周期关系举例" class="headerlink" title="各种周期关系举例"></a>各种周期关系举例</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612084630686.png" alt="image-20230612084630686"></p>
<h2 id="指令周期流程"><a href="#指令周期流程" class="headerlink" title="指令周期流程"></a>指令周期流程</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612084907949.png" alt="image-20230612084907949"></p>
<h2 id="指令周期数据流"><a href="#指令周期数据流" class="headerlink" title="指令周期数据流"></a>指令周期数据流</h2><h3 id="取值周期数据流"><a href="#取值周期数据流" class="headerlink" title="取值周期数据流"></a>取值周期数据流</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612085149460.png" alt="image-20230612085149460"></p>
<h2 id="间址周期数据流"><a href="#间址周期数据流" class="headerlink" title="间址周期数据流"></a>间址周期数据流</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612085454154.png" alt="image-20230612085454154"></p>
<h2 id="执行周期数据流"><a href="#执行周期数据流" class="headerlink" title="执行周期数据流"></a>执行周期数据流</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612085523171.png" alt="image-20230612085523171"></p>
<h2 id="中断周期数据流"><a href="#中断周期数据流" class="headerlink" title="中断周期数据流"></a>中断周期数据流</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612090106825.png" alt="image-20230612090106825"></p>
<h2 id="指令执行方案"><a href="#指令执行方案" class="headerlink" title="指令执行方案"></a>指令执行方案</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612090340151.png" alt="image-20230612090340151"></p>
<h2 id="回顾-1"><a href="#回顾-1" class="headerlink" title="回顾"></a>回顾</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612090453915.png" alt="image-20230612090453915"></p>
<h1 id="数据通路-单总线结构"><a href="#数据通路-单总线结构" class="headerlink" title="数据通路-单总线结构"></a>数据通路-单总线结构</h1><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612101531767.png" alt="image-20230612101531767"></p>
<h1 id="数据通路-专用数据通路"><a href="#数据通路-专用数据通路" class="headerlink" title="数据通路-专用数据通路"></a>数据通路-专用数据通路</h1><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612102617740.png" alt="image-20230612102617740"></p>
<h1 id="硬布线控制器的设计"><a href="#硬布线控制器的设计" class="headerlink" title="硬布线控制器的设计"></a>硬布线控制器的设计</h1><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612103857191.png" alt="image-20230612103857191"></p>
<h2 id="硬布线控制器"><a href="#硬布线控制器" class="headerlink" title="硬布线控制器"></a>硬布线控制器</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612104638659.png" alt="image-20230612104638659"></p>
<h2 id="硬布线控制器的设计-1"><a href="#硬布线控制器的设计-1" class="headerlink" title="硬布线控制器的设计"></a>硬布线控制器的设计</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612105219705.png" alt="image-20230612105219705"></p>
<h3 id="分析每个阶段的微操作序列"><a href="#分析每个阶段的微操作序列" class="headerlink" title="分析每个阶段的微操作序列"></a>分析每个阶段的微操作序列</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612105720911.png" alt="image-20230612105720911"></p>
<h3 id="选择CPU的控制方式"><a href="#选择CPU的控制方式" class="headerlink" title="选择CPU的控制方式"></a>选择CPU的控制方式</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612105804228.png" alt="image-20230612105804228"></p>
<h3 id="安排微操作时序-取指周期"><a href="#安排微操作时序-取指周期" class="headerlink" title="安排微操作时序-取指周期"></a>安排微操作时序-取指周期</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612110206576.png" alt="image-20230612110206576"></p>
<p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612110328864.png" alt="image-20230612110328864"></p>
<h1 id="微程序控制器的基本原理"><a href="#微程序控制器的基本原理" class="headerlink" title="微程序控制器的基本原理"></a>微程序控制器的基本原理</h1><h2 id="微程序控制器的设计思路"><a href="#微程序控制器的设计思路" class="headerlink" title="微程序控制器的设计思路"></a>微程序控制器的设计思路</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612111312942.png" alt="image-20230612111312942"></p>
<h2 id="微程序控制器的基本结构"><a href="#微程序控制器的基本结构" class="headerlink" title="微程序控制器的基本结构"></a>微程序控制器的基本结构</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612112010405.png" alt="image-20230612112010405"></p>
<h2 id="微程序控制器的工作原理"><a href="#微程序控制器的工作原理" class="headerlink" title="微程序控制器的工作原理"></a>微程序控制器的工作原理</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612112502100.png" alt="image-20230612112502100"></p>
<p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230612112708143.png" alt="image-20230612112708143"></p>
<h1 id="微指令设计"><a href="#微指令设计" class="headerlink" title="微指令设计"></a>微指令设计</h1><h2 id="微指令的格式"><a href="#微指令的格式" class="headerlink" title="微指令的格式"></a>微指令的格式</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613143331905.png" alt="image-20230613143331905"></p>
<h2 id="微指令的编码方式"><a href="#微指令的编码方式" class="headerlink" title="微指令的编码方式"></a>微指令的编码方式</h2><h3 id="直接编码方式"><a href="#直接编码方式" class="headerlink" title="直接编码方式"></a>直接编码方式</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613143817431.png" alt="image-20230613143817431"></p>
<h3 id="字段直接编码"><a href="#字段直接编码" class="headerlink" title="字段直接编码"></a>字段直接编码</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613144247810.png" alt="image-20230613144247810"></p>
<p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613144425864.png" alt="image-20230613144425864"></p>
<p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613144439510.png" alt="image-20230613144439510"></p>
<h3 id="字段间接编码"><a href="#字段间接编码" class="headerlink" title="字段间接编码"></a>字段间接编码</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613144627201.png" alt="image-20230613144627201"></p>
<h2 id="微指令地址的形成方式"><a href="#微指令地址的形成方式" class="headerlink" title="微指令地址的形成方式"></a>微指令地址的形成方式</h2><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613145103555.png" alt="image-20230613145103555"></p>
<p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613145308193.png" alt="image-20230613145308193"></p>
<h1 id="微程序控制单元的设计"><a href="#微程序控制单元的设计" class="headerlink" title="微程序控制单元的设计"></a>微程序控制单元的设计</h1><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613145758283.png" alt="image-20230613145758283"></p>
<p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613145937414.png" alt="image-20230613145937414"></p>
<p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613150341263.png" alt="image-20230613150341263"></p>
<h1 id="流水CPU"><a href="#流水CPU" class="headerlink" title="流水CPU"></a>流水CPU</h1><h2 id="指令流水线的基本概念"><a href="#指令流水线的基本概念" class="headerlink" title="指令流水线的基本概念"></a>指令流水线的基本概念</h2><h3 id="指令流水线的定义"><a href="#指令流水线的定义" class="headerlink" title="指令流水线的定义"></a>指令流水线的定义</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613153759369.png" alt="image-20230613153759369"></p>
<h3 id="流水线的表示方法"><a href="#流水线的表示方法" class="headerlink" title="流水线的表示方法"></a>流水线的表示方法</h3><p><img src="/2023/06/13/CPU/Users\小清明十八\AppData\Roaming\Typora\typora-user-images\image-20230613154000813.png" alt="image-20230613154000813"></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><span class="space">&hellip;</span><a class="page-number" href="/page/4/">4</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/2/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">鸣蜩十七</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  




  




  

  <script class="next-config" data-name="enableMath" type="application/json">true</script><script class="next-config" data-name="mathjax" type="application/json">{"enable":true,"tags":"none","js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mathjax/3.2.2/es5/tex-mml-chtml.js","integrity":"sha256-MASABpB4tYktI2Oitl4t+78w/lyA+D7b/s9GEP0JOGI="}}</script>
<script src="/js/third-party/math/mathjax.js"></script>



</body>
</html>
